/******************************************************************************* * Copyright (c) 2005-2009, 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 * *******************************************************************************/ package ch.elexis.data; import static ch.elexis.core.model.LabResultConstants.PATHOLOGIC; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import ch.elexis.core.constants.Preferences; import ch.elexis.core.constants.StringConstants; import ch.elexis.core.data.activator.CoreHub; import ch.elexis.core.data.beans.ContactBean; import ch.elexis.core.exceptions.ElexisException; import ch.elexis.core.jdt.Nullable; import ch.elexis.core.model.IContact; import ch.elexis.core.model.ILabItem; import ch.elexis.core.model.ILabResult; import ch.elexis.core.model.IPatient; import ch.elexis.core.types.Gender; import ch.elexis.core.types.LabItemTyp; import ch.rgw.tools.ExHandler; import ch.rgw.tools.StringTool; import ch.rgw.tools.TimeTool; public class LabResult extends PersistentObject implements ILabResult { public static final String LABRESULT_UNSEEN = "Labresult:unseen"; //$NON-NLS-1$ public static final String DATE = "Datum"; //$NON-NLS-1$ public static final String TIME = "Zeit"; //$NON-NLS-1$ public static final String FLAGS = "Flags"; //$NON-NLS-1$ public static final String COMMENT = "Kommentar"; //$NON-NLS-1$ public static final String RESULT = "Resultat"; //$NON-NLS-1$ public static final String ITEM_ID = "ItemID"; //$NON-NLS-1$ public static final String PATIENT_ID = "PatientID"; //$NON-NLS-1$ public static final String EXTINFO = "ExtInfo"; //$NON-NLS-1$ public static final String UNIT = "unit"; //$NON-NLS-1$ public static final String ANALYSETIME = "analysetime"; //$NON-NLS-1$ public static final String OBSERVATIONTIME = "observationtime"; //$NON-NLS-1$ public static final String TRANSMISSIONTIME = "transmissiontime"; //$NON-NLS-1$ public static final String REFMALE = "refmale"; //$NON-NLS-1$ public static final String REFFEMALE = "reffemale"; //$NON-NLS-1$ public static final String ORIGIN_ID = "OriginID"; //$NON-NLS-1$ private static final String TABLENAME = "LABORWERTE"; //$NON-NLS-1$ private final String SMALLER = "<"; private final String BIGGER = ">"; private static Pattern refValuesPattern = Pattern.compile("\\((.*?)\\)"); //$NON-NLS-1$ private static String[] VALID_ABS_VALUES = new String[] { "positiv", "negativ", "pos.", "neg.", "pos", "neg", ">0", "<0" }; private static final String QUERY_GROUP_ORDER; @Override protected String getTableName(){ return TABLENAME; } static { addMapping(TABLENAME, PATIENT_ID, DATE_COMPOUND, ITEM_ID, RESULT, COMMENT, FLAGS, "Quelle=Origin", TIME, UNIT, ANALYSETIME, OBSERVATIONTIME, TRANSMISSIONTIME, REFMALE, //$NON-NLS-1$ REFFEMALE, ORIGIN_ID); StringBuilder sb = new StringBuilder(); sb.append("SELECT LW.ID, LW." + OBSERVATIONTIME + ", LW." + DATE + ", LW." + TIME + ", "); sb.append("LI." + LabItem.GROUP + ", LI." + LabItem.SHORTNAME + " "); sb.append("FROM " + TABLENAME + " AS LW LEFT JOIN "); sb.append(LabItem.LABITEMS + " AS LI ON LW." + ITEM_ID + "=LI.ID "); sb.append("WHERE LW." + PATIENT_ID + " LIKE ? AND LW.DELETED = '0'"); QUERY_GROUP_ORDER = sb.toString(); } protected LabResult(){} protected LabResult(final String id){ super(id); } /** * @since 3.2 */ public LabResult(IPatient p, TimeTool date, ILabItem item, String result, String comment, IContact origin){ create(null); String[] fields = { PATIENT_ID, DATE, ITEM_ID, RESULT, COMMENT }; String[] vals = new String[] { p.getId(), date == null ? new TimeTool().toString(TimeTool.DATE_GER) : date .toString(TimeTool.DATE_GER), item.getId(), result, comment }; set(fields, vals); // do we have an initial reference value? int flags = isPathologic(p.getGender(), item, result) ? PATHOLOGIC : 0; set(FLAGS, Integer.toString(flags)); // do we have an initial origin (sending facility) if (origin != null) { set(ORIGIN_ID, origin.getId()); } else { set(ORIGIN_ID, ""); } addToUnseen(); } /** * Creates a new LabResult. If the type is numeric, a pathologic check will be applied. * * @param p * @param date * @param item * @param result * @param comment * @param refVal * valid for gender of {@link Patient} p * @param origin * sending facility * @since 3.1 */ public LabResult(final Patient p, final TimeTool date, final ILabItem item, final String result, final String comment, @Nullable String refVal, @Nullable final Kontakt origin){ create(null); String[] fields = { PATIENT_ID, DATE, ITEM_ID, RESULT, COMMENT }; String[] vals = new String[] { p.getId(), date == null ? new TimeTool().toString(TimeTool.DATE_GER) : date .toString(TimeTool.DATE_GER), item.getId(), result, comment }; set(fields, vals); // do we have an initial reference value? if (refVal != null) { if (Person.MALE.equalsIgnoreCase(p.getGeschlecht())) { setRefMale(refVal); } else { setRefFemale(refVal); } } int flags = isPathologic(p.getGender(), item, result) ? PATHOLOGIC : 0; set(FLAGS, Integer.toString(flags)); // do we have an initial origin (sending facility) if (origin != null) { setOrigin(origin); } addToUnseen(); } /** * create a new LabResult. If the type is numeric, we'll check whether it's pathologic */ public LabResult(final Patient p, final TimeTool date, final LabItem item, final String result, final String comment){ this(p, date, item, result, comment, null, null); } /** * Create a new LabResult and set the origin */ public LabResult(final Patient p, final TimeTool date, final LabItem item, final String result, final String comment, final Kontakt origin){ this(p, date, item, result, comment, null, origin); } private boolean isPathologic(final Gender g, final ILabItem item, final String result){ if (item.getTyp().equals(LabItemTyp.ABSOLUTE)) { if (result.toLowerCase().startsWith("pos")) { //$NON-NLS-1$ return true; } if (result.trim().startsWith("+")) { //$NON-NLS-1$ return true; } } else /* if(item.getTyp().equals(LabItem.typ.NUMERIC)) */{ String nr; if (g == Gender.MALE) { nr = getRefMale(); if (nr == null || nr.isEmpty()) { nr = item.getReferenceMale(); } } else { nr = getRefFemale(); if (nr == null || nr.isEmpty()) { nr = item.getReferenceFemale(); } } List<String> refStrings = parseRefString(nr); // only test first string as range is defined in one string if (!refStrings.isEmpty() && result != null) { return testRef(refStrings.get(0), result); } } return false; } public boolean isLongText(){ if (getItem().getTyp() == LabItemTyp.TEXT && getResult().equalsIgnoreCase("text") && !getComment().isEmpty()) { return true; } return false; } private boolean testRef(String ref, String result){ try { if (ref.trim().startsWith(SMALLER) || ref.trim().startsWith(BIGGER)) { String resultSign = null; double refVal = Double.parseDouble(ref.substring(1).trim()); if (result.trim().startsWith(SMALLER) || result.trim().startsWith(BIGGER)) { resultSign = result.substring(0, 1).trim(); result = result.substring(1).trim(); } double val = Double.parseDouble(result); if (ref.trim().startsWith(SMALLER)) { if (val >= refVal && !(val == refVal && SMALLER.equals(resultSign))) { return true; } } else { if (val <= refVal && !(val == refVal && BIGGER.equals(resultSign))) { return true; } } } else { String[] range = ref.split("\\s*-\\s*"); //$NON-NLS-1$ if (range.length == 2) { double lower = Double.parseDouble(range[0]); double upper = Double.parseDouble(range[1]); double val = Double.parseDouble(result); if ((val < lower) || (val > upper)) { return true; } } } } catch (NumberFormatException nfe) { // don't mind } return false; } private static List<String> parseRefString(String ref){ List<String> result = new ArrayList<String>(); Matcher m = refValuesPattern.matcher(ref); while (m.find()) { result.add(m.group(1).trim()); } // add the whole string if nothing found if (result.isEmpty()) { result.add(ref.trim()); } return result; } public static LabResult load(final String id){ return new LabResult(id); } public Patient getPatient(){ return Patient.load(get(PATIENT_ID)); } /** * @deprecated use analysetime, observationtime and transmissiontime */ @Deprecated public String getDate(){ return get(DATE); } @Override public void setDate(String value){ set(DATE, value); } /** * @deprecated use analysetime, observationtime and transmissiontime */ @Deprecated public TimeTool getDateTime(){ String[] vals = get(false, TIME, DATE); return translateDateTime(vals[0], vals[1]); } /** * @since 3.1 */ private static TimeTool translateDateTime(String time, String date){ if ((time == null) || ("".equals(time))) //$NON-NLS-1$ time = "000000"; //$NON-NLS-1$ while (time.length() < 6) { time += StringConstants.ZERO; } return new TimeTool(date + StringConstants.SPACE + time.substring(0, 2) + StringConstants.COLON + time.substring(2, 4) + StringConstants.COLON + time.substring(4, 6)); } public ILabItem getItem(){ return LabItem.load(get(ITEM_ID)); } @Override public void setItem(ILabItem value){ set(ITEM_ID, value.getId()); } public String getResult(){ if (getItem().getTyp() == LabItemTyp.FORMULA) { String value = null; // get the LabOrder for this LabResult List<LabOrder> orders = LabOrder.getLabOrders((String) null, (String) null, getItem(), this, null, null, null); if (orders != null && !orders.isEmpty()) { value = evaluteWithOrderContext(orders.get(0)); } if (value == null || value.equals("?formel?")) { //$NON-NLS-1$ TimeTool time = getTransmissionTime(); if (time == null) { time = getDateTime(); } value = evaluateWithDateContext(time); } setResult(value); } return checkNull(get(RESULT)); } private String evaluteWithOrderContext(LabOrder order){ String ret = null; try { ret = ((LabItem)getItem()).evaluate(getPatient(), order.getLabResults()); } catch (ElexisException e) { ret = "?formel?"; //$NON-NLS-1$ } return ret; } private String evaluateWithDateContext(TimeTool time){ String ret = null; try { ret = ((LabItem)getItem()).evaluate(getPatient(), time); } catch (ElexisException e) { ret = "?formel?"; //$NON-NLS-1$ } return ret; } public void setResult(final String res){ int flags = isPathologic(getPatient().getGender(), getItem(), res) ? PATHOLOGIC : 0; set(new String[] { RESULT, FLAGS }, new String[] { checkNull(res), Integer.toString(flags) }); } public String getComment(){ return checkNull(get(COMMENT)); } public void setComment(String comment) { set(COMMENT, comment); } public boolean isFlag(final int flag){ return (getFlags() & flag) != 0; } public void setFlag(final int flag, final boolean set){ int flags = getFlags(); if (set) { flags |= flag; } else { flags &= ~(flag); } setInt(FLAGS, flags); } public int getFlags(){ return checkZero(get(FLAGS)); } @Override public void setFlags(int value){ set(FLAGS, Integer.toString(value)); } public String getUnit(){ String ret = checkNull(get(UNIT)); if (ret.isEmpty()) { ret = getItem().getUnit(); } return ret; } public void setUnit(String unit){ set(UNIT, unit); } /** * Time the analyse was performed * * @param time */ public TimeTool getAnalyseTime(){ String timestr = checkNull(get(ANALYSETIME)); if (timestr.isEmpty()) { return null; } else { return new TimeTool(timestr); } } /** * Time the analyse was performed * * @param time */ public void setAnalyseTime(TimeTool time){ set(ANALYSETIME, time.toString(TimeTool.TIMESTAMP)); } /** * Time the specimen / sample was taken * * @param time */ public TimeTool getObservationTime(){ String timestr = checkNull(get(OBSERVATIONTIME)); if (timestr.isEmpty()) { return null; } else { return new TimeTool(timestr); } } /** * Time the specimen / sample was taken * * @param time */ public void setObservationTime(TimeTool time){ if (time != null) { set(OBSERVATIONTIME, time.toString(TimeTool.TIMESTAMP)); } } /** * Time the result was transmitted to Elexis. * * @param time */ public TimeTool getTransmissionTime(){ String timestr = checkNull(get(TRANSMISSIONTIME)); if (timestr.isEmpty()) { return null; } else { return new TimeTool(timestr); } } /** * Time the result was transmitted to Elexis. * * @param time */ public void setTransmissionTime(TimeTool time){ set(TRANSMISSIONTIME, time.toString(TimeTool.TIMESTAMP)); } public String getRefMale(){ return resolvePreferedRefValue(getItem().getReferenceMale(), REFMALE); } public void setRefMale(String value){ set(REFMALE, value); setFlag(PATHOLOGIC, isPathologic(getPatient().getGender(), getItem(), getResult())); } public String getRefFemale(){ return resolvePreferedRefValue(getItem().getReferenceFemale(), REFFEMALE); } public void setRefFemale(String value){ set(REFFEMALE, value); setFlag(PATHOLOGIC, isPathologic(getPatient().getGender(), getItem(), getResult())); } /** * get reference value based on user settings (either from local system (LabItem) or device sent * (LabResult)) * * @param localRef * {@link LabItem} reference * @param refField * male or female field of {@link LabResult} * @return Preferred refValue. Per default reference of {@link LabItem} is returned */ private String resolvePreferedRefValue(String localRef, String refField){ boolean useLocalRefs = CoreHub.userCfg.get(Preferences.LABSETTINGS_CFG_LOCAL_REFVALUES, true); if (useLocalRefs && localRef != null && !localRef.isEmpty()) { return localRef; } else { String ref = checkNull(get(refField)); if (ref.isEmpty()) { log.info("using local LabRefVal [" + localRef + "] as none could be resolved from labResult"); return localRef; } return ref; } } /** * Set arbitrary additional information * * @param key * name of the information * @param value * value of the information */ @SuppressWarnings("unchecked") public void setDetail(final String key, final String value){ Map<Object, Object> ext = getMap(EXTINFO); if (value == null) { ext.remove(key); } else { ext.put(key, value); } setMap(EXTINFO, ext); } /** * retrieve additional information * * @param key * name of the requested information * @return value if the information or null if no information with that name was found */ @SuppressWarnings("unchecked") public String getDetail(final String key){ Map<Object, Object> ext = getMap(EXTINFO); return (String) ext.get(key); } @Override public String getLabel(){ StringBuilder sb = new StringBuilder(); sb.append(getItem().getLabel()).append(", ").append(getDate()).append(": ") //$NON-NLS-1$ //$NON-NLS-2$ .append(getResult()); return sb.toString(); // return getResult(); } public static LabResult getForDate(final Patient pat, final TimeTool date, final LabItem item){ Query<LabResult> qbe = new Query<LabResult>(LabResult.class); qbe.add(ITEM_ID, Query.EQUALS, item.getId()); qbe.add(PATIENT_ID, Query.EQUALS, pat.getId()); qbe.add(DATE, Query.EQUALS, date.toString(TimeTool.DATE_COMPACT)); List<LabResult> res = qbe.execute(); if ((res != null) && (res.size() > 0)) { return res.get(0); } return null; } /** * add a LabResult to the list of unseen LabResults. We do not keep LabResults older than * KEEP_UNSEEN_LAB_RESULTS days in this list. */ public void addToUnseen(){ List<LabResult> o = getUnseen(); LinkedList<String> n = new LinkedList<String>(); n.add(getId()); TimeTool limit = new TimeTool(); try { // We need to catch wrong formatted numbers in KEEP_UNSEEN limit.addHours(-24 * Integer.parseInt(CoreHub.globalCfg.get( Preferences.LABSETTINGS_CFG_KEEP_UNSEEN_LAB_RESULTS, Preferences.DAYS_TO_KEEP_UNSEEN_LAB_RESULTS))); } catch (NumberFormatException nex) { ExHandler.handle(nex); limit.addHours(-24 * 7); } // log.log(limit.dump(),Log.INFOS); TimeTool tr = new TimeTool(); for (LabResult lr : o) { // log.info(lr.getDate()); if (tr.set(lr.getDate())) { if (tr.isAfter(limit)) { n.add(lr.getId()); } } } NamedBlob unseen = NamedBlob.load(LABRESULT_UNSEEN); String results = StringTool.join(n, ","); //$NON-NLS-1$ unseen.putString(results); // unseen.set("lastupdate", new TimeTool().toString(TimeTool.TIMESTAMP)); } /** * Remove a lab result from the list of unseen results. */ public void removeFromUnseen(){ NamedBlob unseen = NamedBlob.load(LABRESULT_UNSEEN); String results = unseen.getString(); results = results.replaceAll(getId(), StringTool.leer); unseen.putString(results.replaceAll(",,", ",")); //$NON-NLS-1$ //$NON-NLS-2$ // unseen.set("lastupdate", new TimeTool().toString(TimeTool.TIMESTAMP)); } /** * Return a List of unseen LabResults * * @return */ public static List<LabResult> getUnseen(){ LinkedList<LabResult> ret = new LinkedList<LabResult>(); NamedBlob unseen = NamedBlob.load(LABRESULT_UNSEEN); String results = unseen.getString(); if (results.length() > 0) { for (String id : results.split(",")) { //$NON-NLS-1$ LabResult lr = load(id); if (lr.exists()) { ret.add(lr); } } } return ret; } /** * return the time when the last change to the list of unseen results was made * * @return a timestamp (as in System.CurrentTimeMillis()) */ public static long getLastUpdateUnseen(){ NamedBlob unseen = NamedBlob.load(LABRESULT_UNSEEN); long lastup = unseen.getLastUpdate(); return lastup; } @Override public String toString(){ return getLabel(); } @Override public IContact getOriginContact(){ Kontakt origin = getOrigin(); if(origin==null) { return null; } return new ContactBean(origin); } @Override public void setOriginContact(IContact value){ Kontakt load = Kontakt.load(value.getId()); setOrigin(load); } public void setOrigin(Kontakt origin){ if (origin != null && origin.exists()) { set(ORIGIN_ID, origin.getId()); } else { set(ORIGIN_ID, ""); //$NON-NLS-1$ } } public Kontakt getOrigin(){ String id = get(ORIGIN_ID); if (id != null && !id.isEmpty()) { return Kontakt.load(id); } return null; } public static boolean isValidNumericRefValue(String value){ List<String> refs = parseRefString(value); for (String string : refs) { try { if (string.trim().startsWith("<") || string.trim().startsWith(">")) { //$NON-NLS-1$ //$NON-NLS-2$ Double.parseDouble(string.substring(1).trim()); } else { String[] range = string.split("\\s*-\\s*"); //$NON-NLS-1$ if (range.length == 2) { Double.parseDouble(range[0]); Double.parseDouble(range[1]); } else { return false; } } } catch (NumberFormatException nfe) { return false; } } return true; } public static boolean isValidAbsoluteRefValue(String value){ for (String string : VALID_ABS_VALUES) { if (value.trim().equals(string)) { return true; } } return false; } /** * LabResults grouped in HashMaps. 1st grouped by group, 2nd grouped by item, 3rd grouped by * date containing a list of results. * * @param pat * @return */ public static HashMap<String, HashMap<String, HashMap<String, List<LabResult>>>> getGrouped( Patient pat){ HashMap<String, HashMap<String, HashMap<String, List<LabResult>>>> ret = new HashMap<String, HashMap<String, HashMap<String, List<LabResult>>>>(); if (pat == null) { return ret; } PreparedStatement ps = PersistentObject.getConnection().getPreparedStatement(QUERY_GROUP_ORDER); try { ps.setString(1, pat.getId()); log.debug(ps.toString()); ResultSet resi = ps.executeQuery(); while ((resi != null) && (resi.next() == true)) { String val_id = resi.getString(1); // ID String val_ot = resi.getString(2); // Observationtime String val_date = resi.getString(3); // datum String val_time = resi.getString(4); // zeit String val_group = getNotNull(resi, 5); // grupppe String val_item = getNotNull(resi, 6); // kurzel HashMap<String, HashMap<String, List<LabResult>>> groupMap = ret.get(val_group); if (groupMap == null) { groupMap = new HashMap<String, HashMap<String, List<LabResult>>>(); ret.put(val_group, groupMap); } HashMap<String, List<LabResult>> itemMap = groupMap.get(val_item); if (itemMap == null) { itemMap = new HashMap<String, List<LabResult>>(); groupMap.put(val_item, itemMap); } TimeTool time = null; if (val_ot != null) { time = new TimeTool(val_ot); } else { time = translateDateTime(val_time, val_date); } String date = time.toString(TimeTool.DATE_COMPACT); List<LabResult> resultList = itemMap.get(date); if (resultList == null) { resultList = new ArrayList<LabResult>(); itemMap.put(date, resultList); } resultList.add(new LabResult(val_id)); } } catch (SQLException e) { log.error("Error in fetching labitem groups", e); } finally { PersistentObject.getConnection().releasePreparedStatement(ps); } return ret; } private static String getNotNull(ResultSet set, int index) throws SQLException{ String ret = set.getString(index); if (ret == null) { ret = ""; } return ret; } public static void changeObservationTime(Patient patient, TimeTool from, TimeTool to){ Query<LabResult> qbe = new Query<LabResult>(LabResult.class); qbe.add(PATIENT_ID, Query.EQUALS, patient.getId()); List<LabResult> res = qbe.execute(); ArrayList<LabResult> changeList = new ArrayList<LabResult>(); for (LabResult labResult : res) { TimeTool obsTime = labResult.getObservationTime(); if (obsTime == null) { obsTime = labResult.getDateTime(); } if (obsTime.isSameDay(from)) { changeList.add(labResult); } } for (LabResult labResult : changeList) { labResult.setObservationTime(to); } } }