/******************************************************************************* * Copyright (c) 2005-2016, G. Weirich and Elexis * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * G. Weirich - initial implementation * MEDEVIT <office@medevit.at> *******************************************************************************/ package ch.elexis.data; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import ch.elexis.core.constants.Preferences; import ch.elexis.core.data.activator.CoreHub; import ch.elexis.core.jdt.NonNull; import ch.elexis.core.model.issue.Priority; import ch.elexis.core.model.issue.ProcessStatus; import ch.elexis.core.model.issue.Type; import ch.elexis.core.model.issue.Visibility; import ch.rgw.tools.ExHandler; import ch.rgw.tools.JdbcLink; import ch.rgw.tools.StringTool; import ch.rgw.tools.TimeTool; /** * Ein Reminder ist eine Erinnerung an etwas. Ein Reminder ist an einen Kontakt gebunden. Ein * Reminder hat ein Fälligkeitsdatum und einen Status Es gibt mehrere Typen von Remindern: * <ul> * <li>Nachricht: Es erscheint am und nach dem Fälligkeitsdatum eine Nachricht auf dem Bildschirm, * die den Reminder anzeigt. Dies solange, bis der status auf "erledigt" oder "unerledigt" gesetzt * wird.</li> * <li>Brief: Es wird am Fälligkeitsdatum ein Brief mit gegebener Vorlage zum angegebenen Kontakt * erstellt.</li> * </ul> * * @author Gerry * @since 3.2 major refactorings */ public class Reminder extends PersistentObject implements Comparable<Reminder> { public static final String TABLENAME = "REMINDERS"; public static final String MESSAGE = "Message"; public static final String RESPONSIBLE = "Responsible"; public static final String FLD_VISIBILITY = "Typ"; public static final String FLD_STATUS = "Status"; public static final String DUE = "Due"; public static final String CREATOR = "Creator"; public static final String KONTAKT_ID = "IdentID"; public static final String FLD_PRIORITY = "priority"; public static final String FLD_ACTION_TYPE = "actionType"; public static final String FLD_SUBJECT = "subject"; public static final String FLD_PARAMS = "Params"; public static final String FLD_JOINT_RESPONSIBLES = "Responsibles"; public enum LabelFields { PAT_ID("PatientNr"), FIRSTNAME("Vorname"), LASTNAME("Name"); private final String text; private LabelFields(final String text){ this.text = text; } @Override public String toString(){ return text; } public String getKontaktEquivalent(){ if (text.equals(FIRSTNAME.toString())) { return Kontakt.FLD_NAME2; } else if (text.equals(LASTNAME.toString())) { return Kontakt.FLD_NAME1; } else { return ""; } } } @Override protected String getTableName(){ return TABLENAME; } static { addMapping(TABLENAME, KONTAKT_ID, CREATOR + "=OriginID", DUE + "=S:D:DateDue", FLD_STATUS, FLD_VISIBILITY, FLD_PARAMS, MESSAGE, RESPONSIBLE, FLD_JOINT_RESPONSIBLES + "=JOINT:ResponsibleID:ReminderID:REMINDERS_RESPONSIBLE_LINK", FLD_PRIORITY, FLD_ACTION_TYPE, FLD_SUBJECT); } Reminder(){/* leer */} private Reminder(final String id){ super(id); } /** * Create a new Reminder. Note: by default, the new reminder will have no responsible. @see * addResponsible * * @param ident * The contact (i.e. Patient) the reminder belongs to. If <code>null</code>, the * reminder will be attributed to the current user, making it a non-patient-related * reminder. * @param due * A date string * @param typ * type of the reminder (@see enum Typ) * @param params * parameters depending on the type of the reminder * @param msg * Text for the reminder * */ public Reminder(Kontakt ident, final String due, final Visibility visibility, final String params, final String msg){ create(null); if (ident == null) { ident = CoreHub.actUser; } set(new String[] { KONTAKT_ID, CREATOR, DUE, FLD_STATUS, FLD_VISIBILITY, FLD_PARAMS, MESSAGE }, new String[] { ident.getId(), CoreHub.actUser.getId(), due, Byte.toString((byte) Visibility.ALWAYS.numericValue()), Byte.toString((byte) visibility.numericValue()), params, msg }); } /** * Add a new user to the list of responsibles for that reminder, if not already in list. The * reminder will show up among the reminders, if one of its responsibles is logged in. * * @param a * the user to add to the list of responsible users */ public void addResponsible(final Anwender a){ for (Anwender anwender : getResponsibles()) { if (anwender.getId().equalsIgnoreCase(a.getId())) return; } addToList(FLD_JOINT_RESPONSIBLES, a.getId(), (String[]) null); } /** * Removes a user from the list of responsibles for that reminder, if the user is in the list. * If the user is not in the list, nothing is done. * * @param a * the user to remove from the list of responsible users */ public void removeResponsible(final Anwender a){ for (Anwender anwender : getResponsibles()) { if (anwender.getId().equalsIgnoreCase(a.getId())) removeFromList(FLD_JOINT_RESPONSIBLES, a.getId()); } } /** Einen Reminder anhand seiner ID aus der Datenbank einlesen */ public static Reminder load(final String id){ return new Reminder(id); } @Override public String getLabel(){ String[] vals = get(true, KONTAKT_ID, DUE, MESSAGE, FLD_SUBJECT); Kontakt k = Kontakt.load(vals[0]); if (vals[3] != null && vals[3].length() > 1) { return vals[1] + " (" + getConfiguredKontaktLabel(k) + "): " + vals[3]; } else { return vals[1] + " (" + getConfiguredKontaktLabel(k) + "): " + vals[2]; } } private String getConfiguredKontaktLabel(Kontakt k){ String[] configLabel = CoreHub.userCfg .get(Preferences.USR_REMINDER_PAT_LABEL_CHOOSEN, LabelFields.LASTNAME.toString()) .split(","); StringBuilder sb = new StringBuilder(); for (int i = 0; i < configLabel.length; i++) { sb.append(k.get(configLabel[i])); if (i != configLabel.length - 1) { sb.append(", "); } } return sb.toString(); } public static ProcessStatus determineCurrentStatus(ProcessStatus givenStatus, TimeTool dueDate){ if (ProcessStatus.CLOSED == givenStatus || ProcessStatus.ON_HOLD == givenStatus) { return givenStatus; } TimeTool now = new TimeTool(); now.chop(3); if (now.isEqual(dueDate)) { return ProcessStatus.DUE; } if (now.isAfter(dueDate)) { return ProcessStatus.OVERDUE; } return givenStatus; } public ProcessStatus getStatus(){ ProcessStatus ps = ProcessStatus.byNumericSafe(get(FLD_STATUS)); return Reminder.determineCurrentStatus(ps, getDateDue()); } public String getMessage(){ return checkNull(get(MESSAGE)); } public void setStatus(ProcessStatus s){ set(FLD_STATUS, Byte.toString((byte) s.ordinal())); } public TimeTool getDateDue(){ TimeTool ret = new TimeTool(get(DUE)); ret.chop(3); return ret; } public boolean isDue(){ TimeTool now = new TimeTool(); TimeTool mine = getDateDue(); if (mine.isEqual(now)) { return true; } return false; } public boolean isOverdue(){ TimeTool now = new TimeTool(); TimeTool mine = getDateDue(); if (mine.isBefore(now)) { return true; } return false; } public List<Anwender> getResponsibles(){ List<String[]> lResp = getList(FLD_JOINT_RESPONSIBLES, new String[0]); ArrayList<Anwender> ret = new ArrayList<Anwender>(lResp.size()); for (String[] r : lResp) { ret.add(Anwender.load(r[0])); } return ret; } public Anwender getCreator(){ return Anwender.load(checkNull(get(CREATOR))); } private static String PS_REMINDERS_RESPONSIBLE = "SELECT r.ID FROM reminders r LEFT JOIN reminders_responsible_link rrl ON (r.id = rrl.ReminderId) WHERE rrl.ResponsibleID = ? AND r.deleted = '0'"; public static List<Reminder> findAllUserIsResponsibleFor(Anwender anwender, boolean showOnlyDueReminders){ Set<Reminder> ret = new HashSet<Reminder>(); // we have to apply a set, as there may exist // multiple equivalent entries in reminders_responsible_link // which resolve to multiple occurences of the same element, due to the left join DBConnection dbConnection = getDefaultConnection(); PreparedStatement ps; if (showOnlyDueReminders) { ps = dbConnection.getPreparedStatement(PS_REMINDERS_RESPONSIBLE + " AND r.DateDue < ?"); } else { ps = dbConnection.getPreparedStatement(PS_REMINDERS_RESPONSIBLE); } try { ps.setString(1, anwender.getId()); if (showOnlyDueReminders) { ps.setString(2, new TimeTool().toString(TimeTool.DATE_COMPACT)); } ResultSet res = ps.executeQuery(); while (res.next()) { Reminder reminder = Reminder.load(res.getString(1)); reminder.setDBConnection(dbConnection); ret.add(reminder); } res.close(); } catch (Exception ex) { ExHandler.handle(ex); return new ArrayList<Reminder>(ret); } finally { try { ps.close(); } catch (SQLException e) { // ignore } dbConnection.releasePreparedStatement(ps); } return new ArrayList<Reminder>(ret); } /** * Alle heute (oder vor heute) fälligen Reminder holen * * @return eine Liste aller fälligen Reminder */ public static List<Reminder> findForToday(){ Query<Reminder> qbe = new Query<Reminder>(Reminder.class); qbe.add(DUE, Query.LESS_OR_EQUAL, new TimeTool().toString(TimeTool.DATE_COMPACT)); qbe.add(FLD_STATUS, Query.NOT_EQUAL, Integer.toString(ProcessStatus.CLOSED.numericValue())); List<Reminder> ret = qbe.execute(); return ret; } /** * Alle Reminder zu einem Patienten holen * * @param p * der Patient * @param responsible * der Verantwortliche oder null: Alle * @return eine Liste aller offenen Reminder dieses Patienten */ public static List<Reminder> findForPatient(final Patient p, final Kontakt responsible){ Query<Reminder> qbe = new Query<Reminder>(Reminder.class); qbe.add(KONTAKT_ID, Query.EQUALS, p.getId()); qbe.add(FLD_STATUS, Query.NOT_EQUAL, Integer.toString(ProcessStatus.CLOSED.numericValue())); qbe.add(DUE, Query.LESS_OR_EQUAL, new TimeTool().toString(TimeTool.DATE_COMPACT)); if (responsible != null) { qbe.startGroup(); qbe.add(RESPONSIBLE, Query.EQUALS, responsible.getId()); qbe.or(); qbe.add(RESPONSIBLE, StringTool.leer, null); qbe.endGroup(); } return qbe.execute(); } /** * Alle Reminder holen, die beim Progammstart gezeigt werden sollen * * @return */ public static List<Reminder> findToShowOnStartup(final Anwender a){ Query<Reminder> qbe = new Query<Reminder>(Reminder.class); qbe.add(DUE, Query.LESS_OR_EQUAL, new TimeTool().toString(TimeTool.DATE_COMPACT)); qbe.add(FLD_STATUS, Query.NOT_EQUAL, Integer.toString(ProcessStatus.CLOSED.numericValue())); qbe.add(FLD_VISIBILITY, Query.EQUALS, Integer.toString(Visibility.POPUP_ON_LOGIN.numericValue())); return qbe.execute(); } private static String PS_REMINDERS_BASE = "SELECT r.ID FROM reminders r LEFT JOIN reminders_responsible_link rrl ON (r.id = rrl.ReminderId) WHERE rrl.ResponsibleID = ? AND r.deleted = '0' AND r.Status != '3'"; /** * Retrieve all reminders the given {@link Anwender} is responsible for. The select can be * limited by providing additional criteria * * @param anwender * if <code>null</code> allocates the current user * @param onlyDue * limit the selection to reminders that are already due * @param patient * limit the selection to reminders concerning a specific {@link Patient} * @param onlyPopup * do return only reminders with {@link Visibility#POPUP_ON_PATIENT_SELECTION} * @return * @since 3.1 */ public static List<Reminder> findOpenRemindersResponsibleFor(@NonNull Anwender anwender, final boolean onlyDue, final Patient patient, final boolean onlyPopup){ if (anwender == null) { anwender = CoreHub.actUser; } Set<Reminder> ret = new HashSet<Reminder>(); // we have to apply a set, as there may exist // multiple equivalent entries in reminders_responsible_link // which resolve to multiple occurences of the same element, due to the left join DBConnection dbConnection = getDefaultConnection(); StringBuilder query = new StringBuilder(PS_REMINDERS_BASE); if (onlyDue) { query.append(" AND r.DateDue < " + JdbcLink.wrap(new TimeTool().toString(TimeTool.DATE_COMPACT))); } if (patient != null) { query.append(" AND r.IdentID = " + patient.getWrappedId()); } PreparedStatement ps = dbConnection.getPreparedStatement(query.toString()); try { ps.setString(1, anwender.getId()); ResultSet res = ps.executeQuery(); while (res.next()) { Reminder reminder = Reminder.load(res.getString(1)); reminder.setDBConnection(dbConnection); if (onlyPopup && (reminder.getVisibility() != Visibility.POPUP_ON_PATIENT_SELECTION)) { continue; } ret.add(reminder); } res.close(); } catch (Exception ex) { ExHandler.handle(ex); return new ArrayList<Reminder>(ret); } finally { try { ps.close(); } catch (SQLException e) { // ignore } dbConnection.releasePreparedStatement(ps); } return new ArrayList<Reminder>(ret); } /** * Alle Reminder holen, die bei einem bestimmten Patienten für einen bestimmten Anwender fällig * sind * * @param p * der Patient * @param a * der Anwender * @param bOnlyPopup * nur die zeigen, die den Typ "Bei Auswahl popup" haben. * @return eine Liste der fälligen Reminder dieses Patienten */ public static List<Reminder> findRemindersDueFor(final Patient p, final Anwender a, final boolean bOnlyPopup){ return findOpenRemindersResponsibleFor(a, true, p, bOnlyPopup); } public Patient getKontakt(){ Patient ret = Patient.load(get(KONTAKT_ID)); if (ret.exists()) { return ret; } return null; } /** * The comparator is used when reminders are inserted chronologically in a sorted set. To allow * multiple different reminders at the same day, we use the id to differentiate reminders with * identical dates. */ public int compareTo(final Reminder r){ int i = getDateDue().compareTo(r.getDateDue()); if (i == 0) { return getId().compareTo(r.getId()); } else { return i; } } @Override public boolean delete(){ getConnection() .exec("DELETE FROM REMINDERS_RESPONSIBLE_LINK WHERE ReminderID=" + getWrappedId()); return super.delete(); } public Visibility getVisibility(){ return Visibility.byNumericSafe(get(FLD_VISIBILITY)); } public void setVisibility(Visibility visibility){ set(FLD_VISIBILITY, Integer.toString(visibility.numericValue())); } public Priority getPriority(){ String priority = get(FLD_PRIORITY); return Priority.byNumericSafe(priority); } public void setPriority(Priority priority){ set(FLD_PRIORITY, Integer.toString(priority.numericValue())); } public Type getActionType(){ String actionType = get(FLD_ACTION_TYPE); return Type.byNumericSafe(actionType); } public void setActionType(Type at){ set(FLD_ACTION_TYPE, Integer.toString(at.numericValue())); } public String getSubject(){ return get(FLD_SUBJECT); } public void setSubject(String subject){ set(FLD_SUBJECT, subject); } }