package entity.prescription; import entity.building.Homes; import entity.info.ResInfoTools; import entity.info.Resident; import entity.system.SYSPropsTools; import gui.GUITools; import op.OPDE; import op.tools.SYSCalendar; import op.tools.SYSConst; import op.tools.SYSTools; import org.apache.commons.logging.Log; import org.apache.log4j.Logger; import org.joda.time.*; import org.joda.time.format.DateTimeFormat; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.Query; import javax.swing.*; import java.math.BigDecimal; import java.text.DateFormat; import java.util.*; /** * Created by IntelliJ IDEA. * User: tloehr * Date: 01.12.11 * Time: 15:49 * To change this template use File | Settings | File Templates. */ public class BHPTools { public static final byte STATE_OPEN = 0; public static final byte STATE_DONE = 1; public static final byte STATE_REFUSED = 2; public static final byte STATE_REFUSED_DISCARDED = 3; private static Logger logger = Logger.getLogger(BHPTools.class); // public static final String[] SHIFT_KEY_TEXT = new String[]{"VERY_EARLY", "EARLY", "LATE", "VERY_LATE"}; // public static final String[] SHIFT_TEXT = new String[]{"nursingrecords.bhp.shift.veryearly", "nursingrecords.bhp.shift.early", "nursingrecords.bhp.shift.late", "nursingrecords.bhp.shift.verylate"}; // public static final String[] TIMEIDTEXTLONG = new String[]{"misc.msg.Time.long", "misc.msg.earlyinthemorning.long", "misc.msg.morning.long", "misc.msg.noon.long", "misc.msg.afternoon.long", "misc.msg.evening.long", "misc.msg.lateatnight.long"}; // public static final String[] TIMEIDTEXTSHORT = new String[]{"misc.msg.Time.short", "misc.msg.earlyinthemorning.short", "misc.msg.morning.short", "misc.msg.noon.short", "misc.msg.afternoon.short", "misc.msg.evening.short", "misc.msg.lateatnight.short"}; // // public static final byte BYTE_TIMEOFDAY = 0; // public static final byte BYTE_EARLY_IN_THE_MORNING = 1; // public static final byte BYTE_MORNING = 2; // public static final byte BYTE_NOON = 3; // public static final byte BYTE_AFTERNOON = 4; // public static final byte BYTE_EVENING = 5; // public static final byte BYTE_LATE_AT_NIGHT = 6; public static final String UIDPREFIX = "__bhp"; public static BHP getLastBHP(Prescription prescription) { EntityManager em = OPDE.createEM(); Query query = em.createQuery("SELECT b FROM BHP b WHERE b.prescription = :prescription AND b.state = :state ORDER BY b.ist DESC"); query.setParameter("prescription", prescription); query.setParameter("state", STATE_DONE); query.setFirstResult(0); query.setMaxResults(1); List<BHP> bhp = query.getResultList(); em.close(); return bhp.isEmpty() ? null : bhp.get(0); } public static BHP getLastBHP(Resident resident, int flag) { EntityManager em = OPDE.createEM(); Query query = em.createQuery("SELECT b FROM BHP b WHERE b.resident = :resident AND b.prescription.intervention.flag = :flag AND b.state = :state AND b.prescription.to > :now ORDER BY b.ist DESC"); query.setParameter("resident", resident); query.setParameter("flag", flag); query.setParameter("now", new Date()); query.setParameter("state", STATE_DONE); query.setFirstResult(0); query.setMaxResults(1); List<BHP> bhp = query.getResultList(); em.close(); return bhp.isEmpty() ? null : bhp.get(0); } public static long getConfirmedBHPs(Prescription prescription) { EntityManager em = OPDE.createEM(); Query query = em.createQuery("SELECT COUNT(bhp) FROM BHP bhp WHERE bhp.prescription = :prescription AND bhp.state <> :status"); query.setParameter("prescription", prescription); query.setParameter("status", STATE_OPEN); long num = (Long) query.getSingleResult(); em.close(); return num; } public static boolean hasBeenUsedAlready(Prescription prescription) { long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); Query query = em.createQuery("SELECT bhp FROM BHP bhp WHERE bhp.prescription = :prescription AND bhp.state <> :status"); query.setParameter("prescription", prescription); query.setParameter("status", STATE_OPEN); query.setMaxResults(1); boolean used = query.getResultList().size() > 0; em.close(); SYSTools.showTimeDifference(begin); return used; } public static Comparator<BHP> getOnDemandComparator() { return (o1, o2) -> { int result = o1.getPrescription().getSituation().getText().toUpperCase().compareTo(o2.getPrescription().getSituation().getText().toUpperCase()); if (result == 0) { result = o1.getPrescription().compareTo(o2.getPrescription()); } // if (result == 0) { // Long l1 = o1.getOutcome4(); // Long l2 = o2.getOutcome4(); // if (l1 != null && l2 != null) { // result = l1.compareTo(l2); // } else { // result = SYSTools.nullCompare(l1, l2); // } // } return result; }; } public static Date getMinDatum(Resident bewohner) { Date date; long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); Query query = em.createQuery("SELECT b FROM BHP b WHERE b.resident = :resident ORDER BY b.bhpid"); query.setParameter("resident", bewohner); query.setMaxResults(1); try { date = ((BHP) query.getSingleResult()).getSoll(); } catch (Exception e) { date = new Date(); } em.close(); SYSTools.showTimeDifference(begin); return date; } /** * Diese Methode erzeugt den Tagesplan für die Behandlungspflegen. Dabei werden alle aktiven Verordnungen geprüft, ermittelt ob sie am betreffenden targetdate auch "dran" sind und dann * werden daraus Einträge in der BHP Tabelle erzeugt. Sie teilt sich die Arbeit mit der <code>erzeugen(EntityManager em, List<VerordnungpSchedule> list, Date targetdate, Date zeit)</code> Methode * * @param em, EntityManager Kontext * @return Anzahl der erzeugten BHPs */ public static int generate(EntityManager em) throws Exception { // String internalClassID = "nursingrecords.bhpimport"; int numbhp = 0; LocalDate lastbhp = new LocalDate().minusDays(1); if (OPDE.getProps().containsKey("LASTBHPIMPORT")) { lastbhp = new LocalDate(DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(OPDE.getProps().getProperty("LASTBHPIMPORT"))); } if (lastbhp.isAfter(new LocalDate())) { throw new IndexOutOfBoundsException("The date of the last import is somewhere in the future. Can't be true."); } if (lastbhp.equals(new LocalDate())) { OPDE.info("Today's BHPImport is already done. Stopping."); System.exit(0); } // if (lastbhp.isAfterNow()) { // throw new IndexOutOfBoundsException("The date of the last import is somewhere in the future. Can't be true."); // } LocalDate targetdate = null; // If (for technical reasons) the lastdfn lies in the past (more than the usual 1 day), // then the generation is interated until the current day. for (int days = 1; days <= Days.daysBetween(lastbhp.plusDays(1), new LocalDate()).getDays() + 1; days++) { targetdate = lastbhp.plusDays(days); Query select = em.createQuery(" " + " SELECT vp FROM PrescriptionSchedule vp " + " JOIN vp.prescription v " + // nur die Verordnungen, die überhaupt gültig sind // das sind die mit Gültigkeit BAW oder Gültigkeit endet irgendwann in der Zukunft. // Das heisst, wenn eine Verordnung heute endet, dann wird sie dennoch eingetragen. // Also alle, die bis EINSCHLIEßLICH heute gültig sind. " WHERE v.situation IS NULL AND v.from <= :andatum AND v.to >= :abdatum " + // und nur diejenigen, deren Referenzdatum nicht in der Zukunft liegt. " AND vp.lDatum <= :ldatum AND v.resident.adminonly <> 2 " + " ORDER BY vp.bhppid "); // Diese Aufstellung ergibt mindestens die heute gültigen Einträge. // Wahrscheinlich jedoch mehr als diese. Anhand des LDatums müssen // die wirklichen Treffer nachher genauer ermittelt werden. OPDE.info(SYSTools.xx("\"nursingrecords.bhpimport\"") + " " + SYSTools.xx("misc.msg.writingto") + ": " + OPDE.getUrl()); select.setParameter("andatum", new Date(SYSCalendar.startOfDay(targetdate.toDate()))); select.setParameter("abdatum", new Date(SYSCalendar.endOfDay(targetdate.toDate()))); select.setParameter("ldatum", new Date(SYSCalendar.endOfDay(targetdate.toDate()))); List<PrescriptionSchedule> list = select.getResultList(); numbhp += generate(em, list, targetdate, true); OPDE.important(em, SYSTools.xx("\"nursingrecords.bhpimport\"") + " " + SYSTools.xx("nursingrecords.bhpimport.completed") + ": " + DateFormat.getDateInstance().format(targetdate.toDate()) + " " + SYSTools.xx("nursingrecords.bhpimport.numCreatedEntities") + ": " + numbhp); } SYSPropsTools.storeProp(em, "LASTBHPIMPORT", DateTimeFormat.forPattern("yyyy-MM-dd").print(targetdate)); return numbhp; } /** * Hiermit werden alle BHP Einträge erzeugt, die sich aus den Verordnungen in der zugehörigen Liste ergeben. Die Liste wird aber vorher * noch darauf geprüft, ob sie auch wirklich an dem besagten targetdate passt. Dabei gilt: * <ol> * <li>Alles was taeglich angeordnet ist (jeden Tag oder jeden soundsovielten Tag)</li> * <li>Alles was woechentlich ist und die Spalte (Attribut) mit dem aktuellen Wochentagsnamen größer null ist.</li> * <li>Monatliche Einträge. Aber nur dann, wenn * <ol> * <li>es der <i>n</i>.te Tag im Monat ist <br/><b>oder</b></li> * <li>oder der <i>n</i>.te Wochentag (z.B. Freitag) im Monat ist</li> * </ol> * </li> * </ol> * <p/> * Diese Methode kann von verschiednenen Seiten aufgerufen werden. Zum einen von der "anderen" erzeugen Methode, die einen vollständigen Tagesplan für * alle BWs erzeugt oder von dem Verordnungs Editor, der seinerseits nur eine einzige Verordnung nachtragen möchte. Auf jeden Fall kann die Liste <code>list</code> * auch Einträge enthalten, die unpassend sind. Sie dient nur der Vorauswahl und wird innerhalb dieser Methode dann genau geprüft. Sie "pickt" sich also * nur die passenden Elemente aus dieser Liste heraus. * * @param em EntityManager Kontext * @param list Liste der VerordnungpScheduleen, die ggf. einzutragen sind. * @param targetdate gibt an, für welches Datum die Einträge erzeugt werden. In der Regel ist das immer der aktuelle Tag. * @param wholeday true, dann wird für den ganzen Tag erzeugt. false, dann ab der aktuellen Zeit. * @return die Anzahl der erzeugten BHPs. */ public static int generate(EntityManager em, List<PrescriptionSchedule> list, LocalDate targetdate, boolean wholeday) { DateTimeZone dtz = DateTimeZone.getDefault(); // String internalClassID = "nursingrecords.bhpimport"; BigDecimal maxrows = new BigDecimal(list.size()); int numbhp = 0; long now = System.currentTimeMillis(); byte aktuelleZeit = SYSCalendar.ermittleZeit(now); BigDecimal row = BigDecimal.ZERO; System.out.println("------------------------------------------"); System.out.println(SYSTools.xx("nursingrecords.bhpimport") + " " + SYSTools.xx("nursingrecords.bhpimport.generationForDate") + ": " + DateFormat.getDateInstance(DateFormat.SHORT).format(targetdate.toDate())); System.out.println(SYSTools.xx("nursingrecords.bhpimport.progress")); for (PrescriptionSchedule pSchedule : list) { int numbhpbefore = numbhp; OPDE.debug("generation for schedule: " + pSchedule.toString()); OPDE.debug("targetdate: " + DateFormat.getDateInstance(DateFormat.SHORT).format(targetdate.toDate())); row = row.add(BigDecimal.ONE); SYSTools.printProgBar(row.divide(maxrows, 2, BigDecimal.ROUND_UP).multiply(new BigDecimal(100)).intValue()); pSchedule = em.merge(pSchedule); em.lock(pSchedule, LockModeType.OPTIMISTIC_FORCE_INCREMENT); em.lock(em.merge(pSchedule.getPrescription()), LockModeType.OPTIMISTIC_FORCE_INCREMENT); em.lock(pSchedule.getPrescription().getResident(), LockModeType.OPTIMISTIC); if (!SYSCalendar.isInFuture(pSchedule.getLDatum()) && (pSchedule.isDaily() || pSchedule.isPassenderWochentag(targetdate.toDate()) || pSchedule.isPassenderTagImMonat(targetdate.toDate()))) { boolean treffer = false; LocalDate ldatum = new LocalDate(pSchedule.getLDatum()); // Genaue Ermittlung der Treffer // ============================= if (pSchedule.isDaily()) { // OPDE.debug("Eine tägliche pSchedule"); // Dann wird das LDatum solange um die gewünschte Tagesanzahl erhöht, bis // der targetdate getroffen wurde oder überschritten ist. while (Days.daysBetween(ldatum, targetdate).getDays() > 0) { ldatum = ldatum.plusDays(pSchedule.getTaeglich()); } // Mich interssiert nur der Treffer, also die Punktlandung auf dem targetdate treffer = Days.daysBetween(ldatum, targetdate).getDays() == 0; } else if (pSchedule.isWeekly()) { // OPDE.debug("Eine wöchentliche pSchedule"); while (Weeks.weeksBetween(ldatum, targetdate).getWeeks() > 0) { ldatum = ldatum.plusWeeks(pSchedule.getWoechentlich()); } // Ein Treffer ist es dann, wenn das Referenzdatum gleich dem targetdate ist ODER es zumindest in der selben Kalenderwoche liegt. // Da bei der Vorauswahl durch die Datenbank nur passende Wochentage überhaupt zugelassen wurden, muss das somit der richtige sein. treffer = Weeks.weeksBetween(ldatum, targetdate).getWeeks() == 0; } else if (pSchedule.isMonthly()) { // OPDE.debug("Eine monatliche pSchedule"); while (Months.monthsBetween(ldatum, targetdate).getMonths() > 0) { ldatum = ldatum.plusMonths(pSchedule.getMonatlich()); } // Ein Treffer ist es dann, wenn das Referenzdatum gleich dem targetdate ist ODER es zumindest im selben Monat desselben Jahres liegt. // Da bei der Vorauswahl durch die Datenbank nur passende Wochentage oder Tage im Monat überhaupt zugelassen wurden, muss das somit der richtige sein. treffer = Months.monthsBetween(ldatum, targetdate).getMonths() == 0; } // Es wird immer erst eine Schicht später eingetragen. Damit man nicht mit bereits // abgelaufenen Zeitpunkten arbeitet. // Bei ganzerTag=true werden all diese booleans zu true und damit neutralisiert. boolean erstAbFM = wholeday || aktuelleZeit == SYSCalendar.BYTE_EARLY_IN_THE_MORNING; boolean erstAbMO = wholeday || erstAbFM || aktuelleZeit == SYSCalendar.BYTE_MORNING; boolean erstAbMI = wholeday || erstAbMO || aktuelleZeit == SYSCalendar.BYTE_NOON; boolean erstAbNM = wholeday || erstAbMI || aktuelleZeit == SYSCalendar.BYTE_AFTERNOON; boolean erstAbAB = wholeday || erstAbNM || aktuelleZeit == SYSCalendar.BYTE_EVENING; boolean erstAbNA = wholeday || erstAbAB || aktuelleZeit == SYSCalendar.BYTE_LATE_AT_NIGHT; boolean uhrzeitOK = wholeday || (pSchedule.getUhrzeit() != null && DateTimeComparator.getTimeOnlyInstance().compare(pSchedule.getUhrzeit(), new DateTime(now)) > 0); if (treffer) { if (erstAbFM && pSchedule.getNachtMo().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_EARLY_IN_THE_MORNING, pSchedule.getNachtMo())); numbhp++; } if (erstAbMO && pSchedule.getMorgens().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_MORNING, pSchedule.getMorgens())); numbhp++; } if (erstAbMI && pSchedule.getMittags().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_NOON, pSchedule.getMittags())); numbhp++; } if (erstAbNM && pSchedule.getNachmittags().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_AFTERNOON, pSchedule.getNachmittags())); numbhp++; } if (erstAbAB && pSchedule.getAbends().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_EVENING, pSchedule.getAbends())); numbhp++; } if (erstAbNA && pSchedule.getNachtAb().compareTo(BigDecimal.ZERO) > 0) { em.merge(new BHP(pSchedule, targetdate.toDate(), SYSCalendar.BYTE_LATE_AT_NIGHT, pSchedule.getNachtAb())); numbhp++; } if (uhrzeitOK && pSchedule.getUhrzeit() != null) { // Correction for Daylight Savings LocalTime timeofday = new LocalTime(pSchedule.getUhrzeit()); LocalDateTime localTargetDateTime = targetdate.toLocalDateTime(timeofday); if (dtz.isLocalDateTimeGap(localTargetDateTime)) { //todo: find a better way to calculate this (getOffsetFromLocal) localTargetDateTime = localTargetDateTime.plusHours(1); OPDE.info(SYSTools.xx("Correcting for DST. [BHPPID=" + pSchedule.getBhppid() + "] " + localTargetDateTime.toString())); } em.merge(new BHP(pSchedule, localTargetDateTime.toDate(), SYSConst.UZ, pSchedule.getUhrzeitDosis())); numbhp++; } // Nun noch das LDatum in der Tabelle DFNpSchedule neu setzen. pSchedule.setLDatum(targetdate.toDate()); } } OPDE.debug("number of bhps for this run: " + Integer.toString(numbhp - numbhpbefore)); } System.out.println(); System.out.println(SYSTools.xx("nursingrecords.bhpimport.numCreatedEntities") + " [" + DateFormat.getDateInstance(DateFormat.SHORT).format(targetdate.toDate()) + "]: " + numbhp); System.out.println("------------------------------------------"); OPDE.debug("number of bhps overall: " + Integer.toString(numbhp)); OPDE.debug("------------------------------------------"); return numbhp; } /** * retrieves a list of BHPs for a given resident for a given day. Only OnDemand prescriptions are used (not regular ones) * This method creates a list of existing BHPs, as well as possible appliable BHPs which may be clicked by the user. * * @param resident * @param date * @return */ public static ArrayList<BHP> getBHPsOnDemand(Resident resident, Date date) { List<Prescription> listPrescriptions = PrescriptionTools.getOnDemandPrescriptions(resident, date); LocalDate lDate = new LocalDate(date); long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = new ArrayList<BHP>(); try { Date now = new Date(); String jpql = " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.prescription = :prescription " + " AND bhp.soll >= :from AND bhp.soll <= :to AND bhp.dosis > 0 "; Query queryOnDemand = em.createQuery(jpql); for (Prescription prescription : listPrescriptions) { queryOnDemand.setParameter("prescription", prescription); queryOnDemand.setParameter("from", lDate.toDateTimeAtStartOfDay().toDate()); queryOnDemand.setParameter("to", SYSCalendar.eod(lDate).toDate()); ArrayList<BHP> listBHP4ThisPrescription = new ArrayList<BHP>(queryOnDemand.getResultList()); PrescriptionSchedule schedule = prescription.getPrescriptionSchedule().get(0); // On Demand prescriptions have exactly one schedule, hence the .get(0). // There may not be more than MaxAnzahl BHPs resulting from this prescription. if (listBHP4ThisPrescription.size() < schedule.getMaxAnzahl()) { // Still some BHPs to go ? for (int i = listBHP4ThisPrescription.size(); i < schedule.getMaxAnzahl(); i++) { BHP bhp = new BHP(schedule); bhp.setIst(now); bhp.setSoll(date); bhp.setSollZeit(SYSCalendar.BYTE_TIMEOFDAY); bhp.setDosis(schedule.getMaxEDosis()); bhp.setState(BHPTools.STATE_OPEN); listBHP4ThisPrescription.add(bhp); } } listBHP.addAll(listBHP4ThisPrescription); // outcome BHPs // listBHP.addAll(new ArrayList<BHP>(queryOutcome.getResultList())); } Collections.sort(listBHP, getOnDemandComparator()); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } SYSTools.showTimeDifference(begin); return listBHP; } /** * retrieves BHPs for a prescription <b>WITHOUT</b> outcomes * * @param prescription * @param from * @param to * @return */ public static ArrayList<BHP> getBHPs(Prescription prescription, LocalDate from, LocalDate to) { long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = null; try { Date now = new Date(); String jpql = " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.prescription = :prescription " + " AND bhp.outcome4 IS NULL" + " AND bhp.soll >= :from AND bhp.soll <= :to " + " ORDER BY bhp.soll "; Query queryOnDemand = em.createQuery(jpql); queryOnDemand.setParameter("prescription", prescription); queryOnDemand.setParameter("from", from.toDateTimeAtStartOfDay().toDate()); queryOnDemand.setParameter("to", SYSCalendar.eod(to).toDate()); listBHP = new ArrayList<BHP>(queryOnDemand.getResultList()); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } SYSTools.showTimeDifference(begin); return listBHP; } /** * retrieves a list of BHPs for a given resident for a given day. Only regular prescriptions are used (not OnDemand). * Outcome BHPs included, even if they originate from onDemand Prescriptions. * * @param resident * @param date * @return */ public static ArrayList<BHP> getBHPs(Resident resident, Date date) { // long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = null; try { String jpql = " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.resident = :resident AND bhp.prescription.situation IS NULL" + " AND bhp.soll >= :von AND bhp.soll <= :bis "; Query query = em.createQuery(jpql); LocalDate lDate = new LocalDate(date); query.setParameter("resident", resident); query.setParameter("von", lDate.toDateTimeAtStartOfDay().toDate()); query.setParameter("bis", SYSCalendar.eod(lDate).toDate()); listBHP = new ArrayList<BHP>(query.getResultList()); Collections.sort(listBHP); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } // SYSTools.showTimeDifference(begin); return listBHP; } /** * tells us, if the BHP is commented * * @param bhp * @return */ public static BHP getComment(BHP bhp) { if (bhp.getPrescriptionSchedule().getCheckAfterHours() == null) { return null; } if (bhp.isOutcomeText()) { return null; } EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = null; try { String jpql = " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.outcome4 = :outcome4 "; Query query = em.createQuery(jpql); query.setParameter("outcome4", bhp); listBHP = new ArrayList<BHP>(query.getResultList()); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } return listBHP.isEmpty() ? null : listBHP.get(0); } /** * retrieves a list of BHPs for a given resident for a given day. Only regular prescriptions are used (not OnDemand). * Outcome BHPs included, even if they originate from onDemand Prescriptions. * * @param resident * @param date * @return */ public static ArrayList<BHP> getOutcomeBHPs(Resident resident, LocalDate date) { // long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = null; try { String jpql = " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.resident = :resident AND bhp.outcome4 IS NOT NULL " + " AND bhp.soll >= :von AND bhp.soll <= :bis "; Query query = em.createQuery(jpql); query.setParameter("resident", resident); query.setParameter("von", date.toDateTimeAtStartOfDay().toDate()); query.setParameter("bis", SYSCalendar.eod(date).toDate()); listBHP = new ArrayList<BHP>(query.getResultList()); Collections.sort(listBHP); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } // SYSTools.showTimeDifference(begin); return listBHP; } // public static BHP getOutcome4(BHP bhp) { // long begin = System.currentTimeMillis(); // EntityManager em = OPDE.createEM(); // ArrayList<BHP> listBHP = null; // // try { // // String jpql = " SELECT bhp " + // " FROM BHP bhp " + // " WHERE bhp.outcome4 = :bhp "; // // Query query = em.createQuery(jpql); // // query.setParameter("bhp", bhp); // // listBHP = new ArrayList<>(query.getResultList()); // // } catch (Exception se) { // OPDE.fatal(se); // } finally { // em.close(); // } // SYSTools.showTimeDifference(begin); // return listBHP.isEmpty() ? null : listBHP.get(0); // } public static boolean isOnDemandBHPs(Resident resident, LocalDate date) { EntityManager em = OPDE.createEM(); boolean result = false; try { String jpql = " " + " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.prescription.situation IS NOT NULL " + " AND bhp.resident = :resident " + " AND bhp.outcome4 IS NULL " + " AND bhp.soll >= :from AND bhp.soll <= :to "; Query query = em.createQuery(jpql); query.setParameter("resident", resident); query.setParameter("from", date.toDateTimeAtStartOfDay().toDate()); query.setParameter("to", SYSCalendar.eod(date).toDate()); result = !new ArrayList<BHP>(query.getResultList()).isEmpty(); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } return result; } /** * @param date * @return */ public static ArrayList<BHP> getOpenBHPs(LocalDate date, Homes home) { // long begin = System.currentTimeMillis(); EntityManager em = OPDE.createEM(); ArrayList<BHP> listBHP = null; try { String jpql = " " + " SELECT bhp " + " FROM BHP bhp " + " WHERE bhp.prescription.situation IS NULL AND bhp.state = :state " + " AND bhp.resident.station.home = :home " + " AND bhp.soll >= :from AND bhp.soll <= :to "; Query query = em.createQuery(jpql); query.setParameter("state", STATE_OPEN); query.setParameter("home", home); query.setParameter("from", date.toDateTimeAtStartOfDay().toDate()); query.setParameter("to", SYSCalendar.eod(date).toDate()); listBHP = new ArrayList<BHP>(query.getResultList()); Collections.sort(listBHP); } catch (Exception se) { OPDE.fatal(se); } finally { em.close(); } // SYSTools.showTimeDifference(begin); return listBHP; } public static String getScheduleText(BHP bhp, String prefix, String postfix) { String text = ""; // https://github.com/tloehr/Offene-Pflege.de/issues/63 if (bhp.isOutcomeText()) { text += DateFormat.getTimeInstance(DateFormat.SHORT).format(bhp.getSoll()) + " " + SYSTools.xx("misc.msg.Time.short"); } else if (!bhp.isOnDemand() && !bhp.isOutcomeText()) { if (bhp.getSollZeit() == SYSCalendar.BYTE_TIMEOFDAY) { text += "<font color=\"blue\">" + DateFormat.getTimeInstance(DateFormat.SHORT).format(bhp.getSoll()) + " " + SYSTools.xx("misc.msg.Time.short") + "</font>"; } else { String[] msg = GUITools.getLocalizedMessages(SYSCalendar.TIMEIDTEXTLONG); text += msg[bhp.getSollZeit()]; } } else { if (bhp.getState() == STATE_DONE) { text += DateFormat.getTimeInstance(DateFormat.SHORT).format(bhp.getIst()) + " " + SYSTools.xx("misc.msg.Time.short"); } else { text += "--"; } } return prefix + text + postfix; } public static Icon getIcon(BHP bhp) { if (bhp.getState() == STATE_DONE) { return SYSConst.icon22apply; } if (bhp.getState() == STATE_OPEN) { return null; } if (bhp.getState() == STATE_REFUSED) { return SYSConst.icon22cancel; } if (bhp.getState() == STATE_REFUSED_DISCARDED) { return SYSConst.icon22deleteall; } return null; } public static Icon getWarningIcon(BHP bhp, MedStock stock) { if (!bhp.shouldBeCalculated() || bhp.getPrescription().isClosed()) return null; Icon icon = null; BigDecimal sum = stock == null ? BigDecimal.ZERO : MedStockTools.getSum(stock); if (stock == null) { icon = SYSConst.icon22ledRedOn; } else if (stock.isExpired()) { icon = SYSConst.icon22ledOrangeOn; } else if (!stock.getTradeForm().getDosageForm().isDontCALC() && sum.compareTo(BigDecimal.ZERO) <= 0) { icon = SYSConst.icon22ledYellowOn; } return icon; } /** * see https://offene-pflege.de/doku.php?id=en:dev:pnlbhp#ischangeable * * @param bhp * @return */ public static boolean isChangeable(BHP bhp) { int BHP_MAX_MINUTES_TO_WITHDRAW = Integer.parseInt(OPDE.getProps().getProperty(SYSPropsTools.BHP_MAX_MINUTES_TO_WITHDRAW)); boolean residentAbsent = bhp.getResident().isActive() && ResInfoTools.absentSince(bhp.getResident()) != null; MedInventory inventoryInUse = bhp.hasMed() ? TradeFormTools.getInventory4TradeForm(bhp.getResident(), bhp.getTradeForm()) : null; boolean medTrouble = bhp.shouldBeCalculated() && (inventoryInUse == null || MedStockTools.getStockInUse(inventoryInUse) == null); return !residentAbsent && bhp.getResident().isActive() && !bhp.getPrescription().isClosed() && !medTrouble && (bhp.getUser() == null || (bhp.getUser().equals(OPDE.getMe()) && Minutes.minutesBetween(new DateTime(bhp.getMDate()), new DateTime()).getMinutes() < BHP_MAX_MINUTES_TO_WITHDRAW)) && !bhp.isClosedStockInvolved(); } public static ArrayList<Object[]> getAVGTimesPerDay(LocalDate month) { String mysql = " " + " SELECT bhp.BWKennung i1, (SUM(intv.Dauer) / ?) i4 FROM bhp bhp " + " INNER JOIN prescription ver ON ver.VERID = bhp.VERID " + " INNER JOIN Intervention intv ON ver.MassID = intv.MassID " + " INNER JOIN resident res ON res.BWKennung = bhp.BWKennung " + " WHERE DATE(bhp.Soll) >= ? AND DATE(bhp.Soll) <= ? AND res.StatID IS NOT NULL " + " GROUP BY bhp.BWKennung "; EntityManager em = OPDE.createEM(); Query query = em.createNativeQuery(mysql); DateTime f = month.toDateTimeAtStartOfDay().dayOfMonth().withMinimumValue(); DateTime t = month.toDateTimeAtStartOfDay().dayOfMonth().withMaximumValue().secondOfDay().withMaximumValue(); // OPDE.debug("period " + Days.daysBetween(f, t).getDays() + " days"); query.setParameter(1, Days.daysBetween(f, t).getDays() + 1); query.setParameter(2, f.toDate()); query.setParameter(3, t.toDate()); ArrayList<Object[]> list = new ArrayList(query.getResultList()); em.close(); return list; } public static String getBHPsAsHTMLtable(List<BHP> list, boolean withHeader) { String result = ""; if (!list.isEmpty()) { BHP b1 = list.get(0); if (withHeader) { if (b1.isOnDemand()) { result += SYSConst.html_h2("nursingrecords.bhp.ondemand"); } else if (b1.isOutcomeText()) { result += SYSConst.html_h2("nursingrecords.bhp.outcome"); } else { result += SYSConst.html_h2(SYSCalendar.SHIFT_TEXT[b1.getShift()]); } } String table = ""; if (b1.isOnDemand()) { table += SYSConst.html_table_tr( SYSConst.html_table_th("nursingrecords.nursingprocess.interventions"), SYSConst.html_table_th("misc.msg.state", "center"), SYSConst.html_table_th("misc.msg.outcome", "center") ); } else { table += SYSConst.html_table_tr( SYSConst.html_table_th("nursingrecords.nursingprocess.interventions"), SYSConst.html_table_th("misc.msg.state", "center") ); } for (BHP bhp : list) { String text = PrescriptionTools.getShortDescriptionAsCompactText(bhp.getPrescriptionSchedule().getPrescription()) + (bhp.hasMed() ? ", <b>" + SYSTools.formatBigDecimal(bhp.getDose()) + " " + DosageFormTools.getUsageText(bhp.getPrescription().getTradeForm().getDosageForm()) + "</b>" : "") + (bhp.isOnDemand() || bhp.isOutcomeText() ? "" : getScheduleText(bhp, ", ", "")); if (bhp.isOutcomeText() && bhp.getState() == BHPTools.STATE_DONE) { text += "\n" + SYSConst.html_paragraph(bhp.getText()); } if (b1.isOnDemand()) { String outcomeText = "/"; BHP outcome = getComment(bhp); if (outcome != null && !outcome.isOpen()) { outcomeText = getStateAsHTML(outcome) + " "; outcomeText += (bhp.isOpen() ? "" : outcome.getUser().getUID() + "; " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(outcome.getIst())); outcomeText += (bhp.isOpen() ? "" : "<br/>" + SYSConst.html_paragraph(bhp.getText())); } table += SYSConst.html_table_tr( SYSConst.html_table_td(text, "top"), SYSConst.html_table_td(getStateAsHTML(bhp) + " " + (bhp.isOpen() ? "" : bhp.getUser().getUID() + "; " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(bhp.getIst())), "center"), SYSConst.html_table_td(outcomeText, "center") ); } else { table += SYSConst.html_table_tr( SYSConst.html_table_td(text, "top"), SYSConst.html_table_td(getStateAsHTML(bhp) + " " + (bhp.isOpen() ? "" : bhp.getUser().getUID() + "; " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(bhp.getIst())), "center") ); } } result = SYSConst.html_table(table, "1"); } return result; } private static String getStateAsHTML(BHP bhp) { String html = ""; if (bhp.getState() == STATE_DONE) { html = "✓"; } if (bhp.getState() == STATE_REFUSED) { html = "✗"; } if (bhp.getState() == STATE_OPEN) { html = "❍"; } return html; } /** * a BHP should be confirmed on the same day. unless its during the night shift. Then you can click the BHPs from the nightshift before, until the night shift is over. * * https://github.com/tloehr/Offene-Pflege.de/issues/64 * * @param bhp * @return true, if its too late. false, if we can still access the bhp */ public static boolean bhp2Old(BHP bhp) { DateTime now = new DateTime(); LocalDate day = new LocalDate(bhp.getSoll()); if (now.toLocalDate().equals(day)) return false; if (!now.toLocalDate().minusDays(1).equals(day)) return true; // Only ok, if its yesterday // this can only happen, when the morning part of the night shift is still active, and the BHP in question // belongs to the same shift on the day before. (in the late evening) logger.debug(bhp.getShift()); if (bhp.getShift() == SYSCalendar.SHIFT_VERY_LATE && SYSCalendar.whatShiftIs(now.toDate()) == SYSCalendar.SHIFT_VERY_EARLY) return false; return true; } }