diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.java index 7f3560bb18..ccafc6e032 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/ChooseTriggerDialog.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTem import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTime; import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerWifiSsid; import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerAutosensValue; +import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBolusAgo; public class ChooseTriggerDialog extends DialogFragment { @@ -44,6 +45,7 @@ public class ChooseTriggerDialog extends DialogFragment { add(new TriggerWifiSsid()); add(new TriggerLocation()); add(new TriggerAutosensValue()); + add(new TriggerBolusAgo()); }}; private Unbinder mUnbinder; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java new file mode 100644 index 0000000000..6595d99c36 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgo.java @@ -0,0 +1,153 @@ +package info.nightscout.androidaps.plugins.general.automation.triggers; + +import android.support.v4.app.FragmentManager; +import android.widget.LinearLayout; + +import com.google.common.base.Optional; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DecimalFormat; +import java.util.Date; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.general.automation.elements.Comparator; +import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration; +import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement; +import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder; +import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.JsonHelper; +import info.nightscout.androidaps.utils.T; + +public class TriggerBolusAgo extends Trigger { + private static Logger log = LoggerFactory.getLogger(L.AUTOMATION); + private final double step = 1; + private DecimalFormat decimalFormat = new DecimalFormat("1"); + public InputDuration minutesAgo = new InputDuration(0, InputDuration.TimeUnit.MINUTES); + private Comparator comparator = new Comparator(); + + public TriggerBolusAgo() { + super(); + } + + private TriggerBolusAgo(TriggerBolusAgo triggerBolusAgo) { + super(); + minutesAgo = new InputDuration(triggerBolusAgo.minutesAgo.getMinutes(), InputDuration.TimeUnit.MINUTES); + lastRun = triggerBolusAgo.lastRun; + comparator = new Comparator(triggerBolusAgo.comparator); + } + + public double getValue() { + return minutesAgo.getValue(); + } + + public Comparator getComparator() { + return comparator; + } + + @Override + public synchronized boolean shouldRun() { + + if (lastRun > DateUtil.now() - T.mins(5).msecs()) + return false; + + long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(false); + + if (lastBolusTime == 0) + if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE) + return true; + else + return false; + + double minutesAgo = (double) (DateUtil.now() - lastBolusTime) / (60 * 1000); + log.debug("LastBolus min ago: "+minutesAgo); + + boolean doRun = comparator.getValue().check((minutesAgo), getValue()); + if (doRun) { + if (L.isEnabled(L.AUTOMATION)) + log.debug("Ready for execution: " + friendlyDescription()); + return true; + } + return false; + } + + @Override + public synchronized String toJSON() { + JSONObject o = new JSONObject(); + try { + o.put("type", TriggerBolusAgo.class.getName()); + JSONObject data = new JSONObject(); + data.put("minutesAgo", getValue()); + data.put("lastRun", lastRun); + data.put("comparator", comparator.getValue().toString()); + o.put("data", data); + } catch (JSONException e) { + e.printStackTrace(); + } + return o.toString(); + } + + @Override + Trigger fromJSON(String data) { + try { + JSONObject d = new JSONObject(data); + minutesAgo.setMinutes(JsonHelper.safeGetInt(d, "minutesAgo")); + lastRun = JsonHelper.safeGetLong(d, "lastRun"); + comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator"))); + } catch (Exception e) { + e.printStackTrace(); + } + return this; + } + + @Override + public int friendlyName() { + return R.string.lastboluslabel; + } + + @Override + public String friendlyDescription() { + return MainApp.gs(R.string.lastboluscompared, MainApp.gs(comparator.getValue().getStringRes()), (int) getValue()); + } + + @Override + public Optional icon() { + return Optional.of(R.drawable.icon_bolus); + } + + @Override + public Trigger duplicate() { + return new TriggerBolusAgo(this); + } + + TriggerBolusAgo setValue(int requestedValue) { + this.minutesAgo.setMinutes(requestedValue); + return this; + } + + TriggerBolusAgo lastRun(long lastRun) { + this.lastRun = lastRun; + return this; + } + + TriggerBolusAgo comparator(Comparator.Compare compare) { + this.comparator = new Comparator().setValue(compare); + return this; + } + + @Override + public void generateDialog(LinearLayout root, FragmentManager fragmentManager) { + new LayoutBuilder() + .add(new StaticLabel(R.string.lastboluslabel)) + .add(comparator) + .add(new LabelWithElement(MainApp.gs(R.string.lastboluslabel) + ": ", "", minutesAgo)) + .build(root); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index 2c94d6713d..9eee631e08 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -322,6 +322,22 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return last; } + public long getLastBolusTime(boolean isSMB) { + long now = System.currentTimeMillis(); + long last = 0; + synchronized (treatments) { + for (Treatment t : treatments) { + if (!t.isValid) + continue; + if (t.date > last && t.insulin > 0 && t.isValid && t.date <= now && isSMB == t.isSMB) + last = t.date; + } + } + if (L.isEnabled(L.DATATREATMENTS)) + log.debug("Last manual bolus time: " + new Date(last).toLocaleString()); + return last; + } + @Override public boolean isInHistoryRealTempBasalInProgress() { return getRealTempBasalFromHistory(System.currentTimeMillis()) != null; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c243b09f7..3e87925d53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1409,6 +1409,8 @@ Dist [m]: Name: Location is %1$s + Last bolus ago + Last bolus time %1$s %2$s min ago %1$d day diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgoTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgoTest.java new file mode 100644 index 0000000000..b4965eb1d1 --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/automation/triggers/TriggerBolusAgoTest.java @@ -0,0 +1,120 @@ +package info.nightscout.androidaps.plugins.general.automation.triggers; + +import com.google.common.base.Optional; +import com.squareup.otto.Bus; + +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.AAPSMocker; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.automation.elements.Comparator; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.DateUtil; + +import static org.mockito.ArgumentMatchers.anyDouble; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, Bus.class, ProfileFunctions.class, DateUtil.class, TreatmentsPlugin.class}) +public class TriggerBolusAgoTest { + + long now = 1514766900000L; + + @Test + public void shouldRunTest() { + when(DateUtil.now()).thenReturn(now); // set current time to now + 10 min + TriggerBolusAgo t = new TriggerBolusAgo().setValue(110).comparator(Comparator.Compare.IS_EQUAL).lastRun(now - 1); + Assert.assertFalse(t.shouldRun()); + when(TreatmentsPlugin.getPlugin().getLastBolusTime(false)).thenReturn(now); // Set last bolus time to now + when(DateUtil.now()).thenReturn(now + (10*60*1000)); // set current time to now + 10 min + t = new TriggerBolusAgo().setValue(110).comparator(Comparator.Compare.IS_EQUAL); + Assert.assertEquals(110, t.getValue(), 0.01d); + Assert.assertEquals(Comparator.Compare.IS_EQUAL, t.getComparator().getValue()); + Assert.assertFalse(t.shouldRun()); + t = new TriggerBolusAgo().setValue(10).comparator(Comparator.Compare.IS_EQUAL); + Assert.assertEquals(10, t.getValue(), 0.01d); + Assert.assertTrue(t.shouldRun()); + t = new TriggerBolusAgo().setValue(5).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER); + Assert.assertTrue(t.shouldRun()); + t = new TriggerBolusAgo().setValue(310).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER); + Assert.assertTrue(t.shouldRun()); + t = new TriggerBolusAgo().setValue(420).comparator(Comparator.Compare.IS_EQUAL); + Assert.assertFalse(t.shouldRun()); + t = new TriggerBolusAgo().setValue(390).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER); + Assert.assertTrue(t.shouldRun()); + t = new TriggerBolusAgo().setValue(390).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER); + Assert.assertFalse(t.shouldRun()); + t = new TriggerBolusAgo().setValue(2).comparator(Comparator.Compare.IS_EQUAL_OR_GREATER); + Assert.assertTrue(t.shouldRun()); + t = new TriggerBolusAgo().setValue(390).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER); + Assert.assertTrue(t.shouldRun()); + + when(TreatmentsPlugin.getPlugin().getLastBolusTime(false)).thenReturn(0l); // Set last bolus time to 0 + t = new TriggerBolusAgo().comparator(Comparator.Compare.IS_NOT_AVAILABLE); + Assert.assertTrue(t.shouldRun()); + + t = new TriggerBolusAgo().setValue(214).comparator(Comparator.Compare.IS_EQUAL).lastRun(now - 1); + Assert.assertFalse(t.shouldRun()); + + } + + @Test + public void copyConstructorTest() { + TriggerBolusAgo t = new TriggerBolusAgo().setValue(213).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER); + TriggerBolusAgo t1 = (TriggerBolusAgo) t.duplicate(); + Assert.assertEquals(213, t1.getValue(), 0.01d); + Assert.assertEquals(Comparator.Compare.IS_EQUAL_OR_LESSER, t.getComparator().getValue()); + } + + @Test + public void executeTest() { + TriggerBolusAgo t = new TriggerBolusAgo().setValue(213).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER); + t.executed(1); + Assert.assertEquals(1l, t.getLastRun()); + } + + String LBJson = "{\"data\":{\"comparator\":\"IS_EQUAL\",\"lastRun\":0,\"minutesAgo\":410},\"type\":\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBolusAgo\"}"; + + @Test + public void toJSONTest() { + TriggerBolusAgo t = new TriggerBolusAgo().setValue(410).comparator(Comparator.Compare.IS_EQUAL); + Assert.assertEquals(LBJson, t.toJSON()); + } + + @Test + public void fromJSONTest() throws JSONException { + TriggerBolusAgo t = new TriggerBolusAgo().setValue(410).comparator(Comparator.Compare.IS_EQUAL); + + TriggerBolusAgo t2 = (TriggerBolusAgo) Trigger.instantiate(new JSONObject(t.toJSON())); + Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.getComparator().getValue()); + Assert.assertEquals(410, t2.getValue(), 0.01d); + } + + @Test + public void iconTest() { + Assert.assertEquals(Optional.of(R.drawable.icon_bolus), new TriggerBolusAgo().icon()); + } + + + @Before + public void mock() { + AAPSMocker.mockMainApp(); + AAPSMocker.mockBus(); + AAPSMocker.mockProfileFunctions(); + PowerMockito.mockStatic(DateUtil.class); + AAPSMocker.mockTreatmentPlugin(); + when(DateUtil.now()).thenReturn(now); + + } + +}