package ch.elexis.core.ui.importer.div.importers;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.eclipse.jface.dialogs.Dialog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.elexis.core.data.activator.CoreHub;
import ch.elexis.core.data.events.ElexisEvent;
import ch.elexis.core.data.events.ElexisEventDispatcher;
import ch.elexis.core.data.services.GlobalServiceDescriptors;
import ch.elexis.core.data.services.IDocumentManager;
import ch.elexis.core.data.util.Extensions;
import ch.elexis.core.exceptions.ElexisException;
import ch.elexis.core.importer.div.importers.ILabImportUtil;
import ch.elexis.core.importer.div.importers.ImportHandler;
import ch.elexis.core.importer.div.importers.TransientLabResult;
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.LabItemTyp;
import ch.elexis.core.ui.UiDesk;
import ch.elexis.core.ui.dialogs.KontaktSelektor;
import ch.elexis.core.ui.text.GenericDocument;
import ch.elexis.data.Kontakt;
import ch.elexis.data.LabItem;
import ch.elexis.data.LabMapping;
import ch.elexis.data.LabOrder;
import ch.elexis.data.LabOrder.State;
import ch.elexis.data.LabResult;
import ch.elexis.data.Labor;
import ch.elexis.data.Patient;
import ch.elexis.data.Person;
import ch.elexis.data.Query;
import ch.elexis.data.Xid;
import ch.elexis.hl7.model.TextData;
import ch.elexis.hl7.v26.HL7Constants;
import ch.rgw.tools.TimeTool;
/**
* Utility class that provides basic functionality a Lab importer implementation needs. Lab
* importers should use this class!
*
* @author thomashu
*
*/
public class LabImportUtil implements ILabImportUtil {
private static Logger logger = LoggerFactory.getLogger(LabImportUtil.class);
/**
* Searches for a Labor matching the identifier as part of the Kuerzel or Name attribute. If no
* matching Labor is found, a new Labor is created with identifier as Kuerzel.
*
* @param identifier
* @return
*/
public static Labor getOrCreateLabor(String identifier){
if (identifier == null || identifier.isEmpty()) {
throw new IllegalArgumentException("Labor identifier [" + identifier + "] invalid.");
}
Labor labor = null;
Query<Labor> qbe = new Query<Labor>(Labor.class);
qbe.startGroup();
qbe.add(Kontakt.FLD_SHORT_LABEL, Query.LIKE, "%" + identifier + "%"); //$NON-NLS-1$ //$NON-NLS-2$
qbe.or();
qbe.add(Kontakt.FLD_NAME1, Query.LIKE, "%" + identifier + "%"); //$NON-NLS-1$ //$NON-NLS-2$
qbe.endGroup();
List<Labor> results = qbe.execute();
if (results.isEmpty()) {
labor = new Labor(identifier, "Labor " + identifier); //$NON-NLS-1$
logger.warn(
"Found no Labor for identifier [" + identifier + "]. Created new Labor contact.");
} else {
labor = results.get(0);
if (results.size() > 1) {
logger.warn("Found more than one Labor for identifier [" + identifier
+ "]. This can cause problems when importing results.");
}
}
return labor;
}
/**
* find or set the labor this identifier is linked to
*
* @param identifier
* the value to match to a labor
* @return the found or new set labor contact or null if no lab could be found AND none was
* selected
*/
public static Labor getLinkLabor(String identifier){
if (identifier == null || identifier.isEmpty()) {
throw new IllegalArgumentException("Labor identifier [" + identifier + "] invalid.");
}
Labor labor = null;
// check if there is a connection to an XID
Kontakt k = (Kontakt) Xid.findObject(Kontakt.XID_KONTAKT_LAB_SENDING_FACILITY, identifier);
if (k != null) {
labor = Labor.load(k.getId());
}
// if no connection exists ask to which lab it should be linked
if (labor == null) {
KontaktSelektor ks = new KontaktSelektor(UiDesk.getTopShell(), Labor.class,
Messages.LabImporterUtil_Select,
Messages.LabImporterUtil_SelectLab + " [" + identifier + "]",
Kontakt.DEFAULT_SORT);
if (ks.open() == Dialog.OK) {
labor = (Labor) ks.getSelection();
labor.addXid(Kontakt.XID_KONTAKT_LAB_SENDING_FACILITY, identifier, true);
}
}
return labor;
}
/**
* Searches for a LabItem with an existing LabMapping for the identifier and the labor. If there
* is no LabMapping, for backwards compatibility the LaborId and Kürzel attributes of all
* LabItems will be used to find a match.
*
* @param identifier
* @param labor
* @return
*/
public static LabItem getLabItem(String identifier, Labor labor){
LabMapping mapping = LabMapping.getByContactAndItemName(labor.getId(), identifier);
if (mapping != null) {
return mapping.getLabItem();
}
LabItem labItem = null;
Query<LabItem> qbe = new Query<LabItem>(LabItem.class);
qbe.add(LabItem.LAB_ID, Query.EQUALS, labor.getId());
qbe.add(LabItem.SHORTNAME, Query.EQUALS, identifier);
List<LabItem> list = qbe.execute();
if (!list.isEmpty()) {
labItem = list.get(0);
if (list.size() > 1) {
logger.warn("Found more than one LabItem for identifier [" + identifier
+ "] and Labor [" + labor.getLabel(true)
+ "]. This can cause problems when importing results.");
}
}
return labItem;
}
/**
* Search for a LabResult with matching patient, item and timestamps. The timestamp attributes
* can be null if not relevant for the search, but at least one timestamp has to be specified.
*
* @param patient
* @param timestamp
* @param item
* @return
*/
public static List<LabResult> getLabResults(IPatient patient, ILabItem item, TimeTool date,
TimeTool analyseTime, TimeTool observationTime){
if (date == null && analyseTime == null && observationTime == null) {
throw new IllegalArgumentException("No timestamp specified.");
}
Query<LabResult> qr = new Query<LabResult>(LabResult.class);
qr.add(LabResult.PATIENT_ID, Query.EQUALS, patient.getId());
qr.add(LabResult.ITEM_ID, Query.EQUALS, item.getId());
if (date != null) {
qr.add(LabResult.DATE, Query.EQUALS, date.toString(TimeTool.DATE_GER));
}
if (analyseTime != null) {
qr.add(LabResult.ANALYSETIME, Query.EQUALS, analyseTime.toString(TimeTool.TIMESTAMP));
}
if (observationTime != null) {
qr.add(LabResult.OBSERVATIONTIME, Query.EQUALS,
observationTime.toString(TimeTool.TIMESTAMP));
}
return qr.execute();
}
/**
* Import a list of TransientLabResults. Create LabOrder objects for new results.
*/
public String importLabResults(List<TransientLabResult> results, ImportHandler uiHandler){
boolean overWriteAll = false;
String orderId = LabOrder.getNextOrderId();
for (TransientLabResult transientLabResult : results) {
List<LabResult> existing = getExistingResults(transientLabResult);
if (existing.isEmpty()) {
ILabResult labResult = createLabResult(transientLabResult, orderId);
CoreHub.getLocalLockService().acquireLock((LabResult) labResult);
CoreHub.getLocalLockService().releaseLock((LabResult) labResult);
} else {
for (LabResult labResult : existing) {
if (overWriteAll) {
CoreHub.getLocalLockService().acquireLock((LabResult) labResult);
transientLabResult.overwriteExisting(labResult);
CoreHub.getLocalLockService().releaseLock((LabResult) labResult);
continue;
}
// dont bother user if result has the same value
if (transientLabResult.isSameResult(labResult)) {
logger.info("Result " + labResult.toString() + " already exists.");
continue;
}
ImportHandler.OverwriteState retVal = uiHandler.askOverwrite(
transientLabResult.getPatient(), labResult, transientLabResult);
if (retVal == ImportHandler.OverwriteState.OVERWRITE) {
CoreHub.getLocalLockService().acquireLock((LabResult) labResult);
transientLabResult.overwriteExisting(labResult);
CoreHub.getLocalLockService().releaseLock((LabResult) labResult);
continue;
} else if (retVal == ImportHandler.OverwriteState.OVERWRITEALL) {
overWriteAll = true;
CoreHub.getLocalLockService().acquireLock((LabResult) labResult);
transientLabResult.overwriteExisting(labResult);
CoreHub.getLocalLockService().releaseLock((LabResult) labResult);
continue;
}
}
}
}
ElexisEventDispatcher.getInstance()
.fire(new ElexisEvent(null, LabResult.class, ElexisEvent.EVENT_RELOAD));
return orderId;
}
/**
* Match for existing result with same item and date. Matching dates are checked for validitiy
* (not same as transmission date).
*
* @param transientLabResult
* @return
*/
private static List<LabResult> getExistingResults(TransientLabResult transientLabResult){
List<LabResult> ret = Collections.emptyList();
// don't overwrite documents
if (!transientLabResult.getLabItem().getTyp().equals(LabItemTyp.DOCUMENT)) {
if (transientLabResult.isObservationTime()) {
ret = LabImportUtil.getLabResults(transientLabResult.getPatient(),
transientLabResult.getLabItem(), null, null,
transientLabResult.getObservationTime());
} else if (transientLabResult.isAnalyseTime()) {
ret = LabImportUtil.getLabResults(transientLabResult.getPatient(),
transientLabResult.getLabItem(), null, transientLabResult.getAnalyseTime(),
null);
} else {
ret = LabImportUtil.getLabResults(transientLabResult.getPatient(),
transientLabResult.getLabItem(), transientLabResult.getDate(), null, null);
}
}
return ret;
}
/**
* Create the LabResult from the TransientLabResult. Also lookup if there is a matching
* LabOrder. If it is in State.ORDERED the created LabResult is set to that LabOrder, else
* create a new LabOrder and add the LabResult to it.
*
* @param transientLabResult
* @param orderId
* @return the created lab result element
*/
public ILabResult createLabResult(TransientLabResult transientLabResult, String orderId){
ILabResult labResult = transientLabResult.persist();
List<LabOrder> existing = LabOrder.getLabOrders(transientLabResult.getPatient().getId(),
null, transientLabResult.getLabItem(), null, null, null, State.ORDERED);
LabOrder labOrder = null;
if (existing == null || existing.isEmpty()) {
TimeTool time = transientLabResult.getObservationTime();
if (time == null) {
time = transientLabResult.getDate();
if (time == null) {
logger.warn(
"Could not resolve observation time and time for ILabResult [{}], defaulting to now.",
labResult.getId());
time = new TimeTool();
}
}
labOrder = new LabOrder(CoreHub.actUser.getId(), CoreHub.actMandant.getId(),
transientLabResult.getPatient().getId(), transientLabResult.getLabItem(),
labResult.getId(), orderId, "Import", time);
} else {
// TODO for multiple entries we could check on which one the observationtime matches
labOrder = existing.get(0);
labOrder.setLabResultIdAsString(labResult.getId());
}
labOrder.setState(State.DONE_IMPORT);
return labResult;
}
@Override
public ILabItem getLabItem(String identifier, IContact labor){
LabMapping mapping = LabMapping.getByContactAndItemName(labor.getId(), identifier);
if (mapping != null) {
return mapping.getLabItem();
}
LabItem labItem = null;
Query<LabItem> qbe = new Query<LabItem>(LabItem.class);
qbe.add(LabItem.LAB_ID, Query.EQUALS, labor.getId());
qbe.add(LabItem.SHORTNAME, Query.EQUALS, identifier);
List<LabItem> list = qbe.execute();
if (!list.isEmpty()) {
labItem = list.get(0);
if (list.size() > 1) {
logger.warn(
"Found more than one LabItem for identifier [" + identifier + "] and Labor ["
+ labor.getLabel() + "]. This can cause problems when importing results.");
}
}
return labItem;
}
@Override
public ILabItem createLabItem(String code, String name, IContact labor, String male,
String female, String unit, LabItemTyp typ, String testGroupName,
String nextTestGroupSequence){
return new LabItem(code, name, labor.getId(), male, female, unit, typ, testGroupName,
nextTestGroupSequence);
}
@Override
public ILabItem getDocumentLabItem(String shortname, String name, IContact labor){
Query<LabItem> qbe = new Query<LabItem>(LabItem.class);
qbe.add(LabItem.SHORTNAME, Query.EQUALS, shortname);
qbe.add(LabItem.LAB_ID, Query.EQUALS, labor.getId());
qbe.add(LabItem.TYPE, Query.EQUALS, new Integer(LabItemTyp.DOCUMENT.ordinal()).toString());
LabItem labItem = null;
List<LabItem> itemList = qbe.execute();
if (itemList.size() > 0) {
labItem = itemList.get(0);
}
return labItem;
}
@Override
public void createCommentsLabResult(TextData hl7TextData, IPatient pat, IContact labor,
int number, TimeTool commentDate){
if (hl7TextData.getDate() == null) {
hl7TextData.setDate(commentDate.getTime());
}
TimeTool commentsDate = new TimeTool(hl7TextData.getDate());
// find LabItem
Query<LabItem> qbe = new Query<LabItem>(LabItem.class);
qbe.add(LabItem.LAB_ID, Query.EQUALS, labor.getId());
qbe.add(LabItem.TITLE, Query.EQUALS, HL7Constants.COMMENT_NAME);
qbe.add(LabItem.SHORTNAME, Query.EQUALS, HL7Constants.COMMENT_CODE);
List<LabItem> list = qbe.execute();
LabItem li = null;
if (list.size() < 1) {
// LabItem doesn't yet exist
LabItemTyp typ = LabItemTyp.TEXT;
li = new LabItem(HL7Constants.COMMENT_CODE, HL7Constants.COMMENT_NAME, labor.getId(),
"", "", //$NON-NLS-1$ //$NON-NLS-2$
"", typ, HL7Constants.COMMENT_GROUP, Integer.toString(number)); //$NON-NLS-1$
} else {
li = list.get(0);
}
// add LabResult
Query<LabResult> qr = new Query<LabResult>(LabResult.class);
qr.add(LabResult.PATIENT_ID, Query.EQUALS, pat.getId());
qr.add(LabResult.DATE, Query.EQUALS, commentsDate.toString(TimeTool.DATE_GER));
qr.add(LabResult.ITEM_ID, Query.EQUALS, li.getId());
if (qr.execute().size() == 0) {
StringBuilder comment = new StringBuilder();
if (hl7TextData.getText() != null) {
comment.append(hl7TextData.getText());
}
if (hl7TextData.getComment() != null) {
comment.append(hl7TextData.getComment());
}
// only add coments not yet existing
LabResult labResult =
new LabResult(pat, commentsDate, li, "text", comment.toString(), labor); //$NON-NLS-1$
labResult.setObservationTime(commentsDate);
CoreHub.getLocalLockService().acquireLock((LabResult) labResult);
CoreHub.getLocalLockService().releaseLock((LabResult) labResult);
}
}
@Override
public void createDocumentManagerEntry(String title, String lab, byte[] data, String mimeType,
TimeTool date, IPatient pat){
Object os = Extensions.findBestService(GlobalServiceDescriptors.DOCUMENT_MANAGEMENT);
if (os != null) {
IDocumentManager docManager = (IDocumentManager) os;
//find or create a category for this lab
boolean catIsNotExisting = true;
for (String cat : docManager.getCategories()) {
if (cat.equals(lab)) {
catIsNotExisting = false;
break;
}
}
if (catIsNotExisting) {
docManager.addCategorie(lab);
}
// add document
try {
Patient patient = Patient.load(pat.getId());
docManager.addDocument(new GenericDocument(patient, title, lab, data,
date.toString(TimeTool.DATE_GER), null, mimeType));
} catch (IOException | ElexisException e) {
logger.error("Error saving document received via hl7 in local document manager", e);
}
}
}
@Override
public ILabResult createLabResult(IPatient patient, TimeTool date, ILabItem labItem,
String result, String comment, String refVal, IContact origin){
Patient pat = Patient.load(patient.getId());
LabItem item = LabItem.load(labItem.getId());
Labor labor = Labor.load(origin.getId());
logger.debug("Creating result with patient [" + pat.getId() + "] labitem [" + item.getId()
+ "] origin [" + labor.getId() + "]");
LabResult labResult = new LabResult(pat, date, item, result, comment, labor);
if (refVal != null) {
if (Person.MALE.equalsIgnoreCase(pat.getGeschlecht())) {
labResult.setRefMale(refVal);
} else {
labResult.setRefFemale(refVal);
}
}
// TODO LockHook too early
return labResult;
}
}