/* * The MIT License * * Copyright 2015 Apothesource, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.apothesource.pillfill.datamodel; import com.apothesource.pillfill.exception.ReminderConfigurationException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; /* * Copyright 2015, Apothesource Inc. * All Rights Reserved. * @author Michael Ramirez (michael@pillfill.com) */ /** * @author Michael Ramirez (michael@pillfill.com) */ public class Reminder { private static final String CRON_TEMPLATE = "%d %d %d * * %s"; public String rxId; public String reminderId; public Set<String> deviceUuids = new HashSet<>(); public Set<UserDevice> deviceList = new HashSet<>(); public ReminderState state = ReminderState.CREATED; public ReminderType type = ReminderType.MED_REMINDER; public Integer fireHour; public Integer fireMinute; public Integer fireDays; public String subjectName; public String lang; public Reminder fallbackReminder; public String fallbackReminderId; public Integer waitTimeMinutes; public boolean debug = false; public long creationDate; public long lastFireDate; public String timeZone; public int utcMinOffset; public Set<String> mgrUuids = new HashSet<>(); public String _rev; public static ArrayList<Reminder> updateAndValidateReminder(Reminder rm, PrescriptionType rx, int maxDepth) throws ReminderConfigurationException { return validateReminder(rm, rx, null, 0, maxDepth); } /** * Function to validate and link reminders into a flattened list * * @param rm The reminder to be schedule * @param rx The parent prescription for the reminder * @param reminderId A reminder ID for this reminder. May be null. * @param depth The current depth of the fallback reminder. Used to ensure that reminders are not nested too deeply. * @param maxDepth The maximum nesting level for fallback reminders * @return A list of reminders including this reminder and all child reminders (fallback) */ public static ArrayList<Reminder> validateReminder(Reminder rm, PrescriptionType rx, String reminderId, int depth, int maxDepth) throws ReminderConfigurationException { ArrayList<Reminder> reminderList = new ArrayList<>(); rm.state = ReminderState.ACTIVE; rm.rxId = rx.getUuid(); rm.creationDate = new Date().getTime(); rm.rxId = rx.getUuid(); rm.reminderId = (reminderId != null) ? reminderId : UUID.randomUUID().toString(); if ((rm.deviceList == null || rm.deviceList.isEmpty()) && rm.deviceUuids.isEmpty()) throw new ReminderConfigurationException("Reminders must have at least one device associated with it."); if (rx.getComputedInactiveAfterDate().before(new Date())) throw new ReminderConfigurationException("Associated prescription ID is already inactive."); if (rm.type != ReminderType.MED_REMINDER && rm.fallbackReminder != null) throw new ReminderConfigurationException("Only MED_REMINDER types can have fallbacks."); if (depth == 0) { if (rm.fireHour == null || rm.fireMinute == null) throw new ReminderConfigurationException("Primary reminders must have a fire time defined."); rm.waitTimeMinutes = null; if (rm.fireDays == null) rm.fireDays = FireDay.EVERYDAY; } else if (depth < maxDepth) { rm.fireHour = null; rm.fireMinute = null; rm.fireDays = null; if (rm.waitTimeMinutes == null) rm.waitTimeMinutes = 15; } else { throw new ReminderConfigurationException(String.format("You can only have %d failback reminders.", maxDepth)); } //We've validated this reminder, let's check failbacks reminderList.add(rm); if (rm.fallbackReminder != null) { Reminder failbackReminder = rm.fallbackReminder; rm.fallbackReminder = null; String failbackId = UUID.randomUUID().toString(); rm.fallbackReminderId = failbackId; reminderList.addAll(validateReminder(failbackReminder, rx, failbackId, ++depth, maxDepth)); } return reminderList; } public String toCronString() { return toCronString(!debug); } protected String toCronString(boolean randomSecond) { int randomSecondTrigger = randomSecond ? (int) (Math.random() * 60) : 0; return String.format(CRON_TEMPLATE, randomSecondTrigger, fireMinute, fireHour, FireDay.getFireDaysCronString(fireDays)); } public boolean isPrimaryReminder() { return fireHour != null && fireMinute != null; } public enum ReminderState { CREATED, ACTIVE, INACTIVE, DELETED, INVALID } public enum ReminderType { MED_REMINDER, MED_REFILL_DUE, MED_QUANTITY_LOW, MISSED_REMINDER, MED_REMINDER_DEACTIVATED } public enum FireDay { SUN(0b0000001), MON(0b0000010), TUE(0b0000100), WED(0b0001000), THU(0b0010000), FRI(0b0100000), SAT(0b1000000); public static int EVERYDAY = 127; int day = 0; FireDay(int day) { this.day = day; } static public String getFireDaysCronString(int days) { if (days == EVERYDAY) { return "?"; } else { return Arrays.asList(FireDay.values()).stream() .filter(fireDay -> (fireDay.day & days) > 0) //See if this day bit is set .map((FireDay fireDay) -> fireDay.name()) //If so, convert day to a string .collect(Collectors.joining(",")); //Then concat them into a single string list } } } public static class ReminderWSResponse { public List<Reminder> reminders; public String error; } //0=Sun //1=Mon //2=Tue //3=Wed //4=Thu //5=Fri //6=Sat }