package at.medevit.elexis.ehc.docbox.service; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.ehealth_connector.cda.ch.AbstractCdaCh; import org.ehealth_connector.common.Organization; import org.ehealth_connector.common.Telecoms; import org.ehealth_connector.common.enums.AddressUse; import org.openhealthtools.mdht.uml.cda.Act; import org.openhealthtools.mdht.uml.cda.Author; import org.openhealthtools.mdht.uml.cda.CDAFactory; import org.openhealthtools.mdht.uml.cda.ClinicalDocument; import org.openhealthtools.mdht.uml.cda.Consumable; import org.openhealthtools.mdht.uml.cda.EntryRelationship; import org.openhealthtools.mdht.uml.cda.InfrastructureRootTypeId; import org.openhealthtools.mdht.uml.cda.ManufacturedProduct; import org.openhealthtools.mdht.uml.cda.Material; import org.openhealthtools.mdht.uml.cda.PatientRole; import org.openhealthtools.mdht.uml.cda.Section; import org.openhealthtools.mdht.uml.cda.SubstanceAdministration; import org.openhealthtools.mdht.uml.cda.Supply; import org.openhealthtools.mdht.uml.cda.util.CDAUtil; import org.openhealthtools.mdht.uml.hl7.datatypes.CD; import org.openhealthtools.mdht.uml.hl7.datatypes.CE; import org.openhealthtools.mdht.uml.hl7.datatypes.DatatypesFactory; import org.openhealthtools.mdht.uml.hl7.datatypes.ED; import org.openhealthtools.mdht.uml.hl7.datatypes.II; import org.openhealthtools.mdht.uml.hl7.datatypes.IVL_PQ; import org.openhealthtools.mdht.uml.hl7.datatypes.IVL_TS; import org.openhealthtools.mdht.uml.hl7.datatypes.PIVL_TS; import org.openhealthtools.mdht.uml.hl7.datatypes.PQ; import org.openhealthtools.mdht.uml.hl7.datatypes.ST; import org.openhealthtools.mdht.uml.hl7.datatypes.TEL; import org.openhealthtools.mdht.uml.hl7.datatypes.TS; import org.openhealthtools.mdht.uml.hl7.vocab.ActClassSupply; import org.openhealthtools.mdht.uml.hl7.vocab.NullFlavor; import org.openhealthtools.mdht.uml.hl7.vocab.SetOperator; import org.openhealthtools.mdht.uml.hl7.vocab.x_ActClassDocumentEntryAct; import org.openhealthtools.mdht.uml.hl7.vocab.x_ActRelationshipEntryRelationship; import org.openhealthtools.mdht.uml.hl7.vocab.x_DocumentActMood; import org.openhealthtools.mdht.uml.hl7.vocab.x_DocumentSubstanceMood; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.medevit.elexis.ehc.core.EhcCoreMapper; import at.medevit.elexis.ehc.core.EhcCoreService; import ch.elexis.core.services.IFormattedOutput; import ch.elexis.core.services.IFormattedOutputFactory; import ch.elexis.core.services.IFormattedOutputFactory.ObjectType; import ch.elexis.core.services.IFormattedOutputFactory.OutputType; import ch.elexis.data.Mandant; import ch.elexis.data.Person; import ch.elexis.data.Prescription; import ch.elexis.data.Rechnungssteller; import ch.elexis.data.Rezept; import ch.elexis.docbox.ws.client.SendClinicalDocumentClient; import ch.rgw.tools.StringTool; import ch.rgw.tools.TimeTool; @Component public class DocboxService { private static final Logger logger = LoggerFactory.getLogger(DocboxService.class); public static final String DOMAIN_KSK = "www.xid.ch/id/ksk"; //$NON-NLS-1$ public static final String[] DOSE_TIME = { "190001010800", "190001011200", "190001011600", "190001012000" }; private static EhcCoreService ehcCoreService; @Reference public synchronized void setEhcCoreService(EhcCoreService service){ ehcCoreService = service; } public synchronized void unsetEhcCoreService(EhcCoreService service){ if (ehcCoreService == service) { ehcCoreService = null; } } private static IFormattedOutputFactory foFactory; @Reference public synchronized void setFormattedOutputFactory(IFormattedOutputFactory service){ foFactory = service; } public synchronized void unsetFormattedOutputFactory(IFormattedOutputFactory service){ if (foFactory == service) { foFactory = null; } } public static AbstractCdaCh<?> getPrescriptionDocument(Rezept rezept){ AbstractCdaCh<?> document = ehcCoreService.createCdaChDocument(rezept.getPatient(), rezept.getMandant()); ClinicalDocument clinicalDocument = document.getDocRoot().getClinicalDocument(); // clear template ids for now clinicalDocument.getTemplateIds().clear(); // CDA type and template id InfrastructureRootTypeId typeId = CDAFactory.eINSTANCE.createInfrastructureRootTypeId(); typeId.setRoot("2.16.840.1.113883.1.3"); typeId.setExtension("POCD_HD000040"); clinicalDocument.setTypeId(typeId); II id = DatatypesFactory.eINSTANCE.createII("2.16.756.5.30.1.1.1.1"); id.setExtension("CDA-CH"); clinicalDocument.setId(id); clinicalDocument.getTemplateIds().add(id); id = DatatypesFactory.eINSTANCE.createII("2.16.756.5.30.1.105"); id.setExtension("SwissMedicalSuite_ERezept"); clinicalDocument.setId(id); clinicalDocument.getTemplateIds().add(id); // set loinc code CE loinccode = DatatypesFactory.eINSTANCE.createCE(); loinccode.setCodeSystem("2.16.840.1.113883.6.1"); loinccode.setCode("57833-6"); loinccode.setDisplayName("Prescriptions"); loinccode.setCodeSystemName("LOINC"); clinicalDocument.setCode(loinccode); // RezeptID id = DatatypesFactory.eINSTANCE.createII("2.16.756.5.30.1.105.1.6"); id.setExtension(getRezeptId(rezept)); clinicalDocument.setId(id); // Ausstellungsdatum TS timestamp = DatatypesFactory.eINSTANCE.createTS(); TimeTool rezeptDate = new TimeTool(rezept.getDate()); timestamp.setValue(rezeptDate.toString(TimeTool.TIMESTAMP)); clinicalDocument.setEffectiveTime(timestamp); // confidentiality CE confidentiality = DatatypesFactory.eINSTANCE.createCE(); confidentiality.setCodeSystem("2.16.840.1.113883.5.25"); confidentiality.setCode("N"); clinicalDocument.setConfidentialityCode(confidentiality); // add empty id to patient role PatientRole patientRole = clinicalDocument.getPatientRoles().get(0); patientRole.getIds().add(DatatypesFactory.eINSTANCE.createII()); // add empty time to author Author author = clinicalDocument.getAuthors().get(0); author.setTime(DatatypesFactory.eINSTANCE.createTS()); // Patient und Arzt bereits gesetzt, darum custodian Rechnungssteller rechnungssteller = rezept.getMandant().getRechnungssteller(); Organization organization = null; if (rechnungssteller.istOrganisation()) { organization = new Organization(rechnungssteller.get(Rechnungssteller.FLD_NAME1) + " " + rechnungssteller.get(Rechnungssteller.FLD_NAME2)); } else { organization = new Organization(rechnungssteller.get(Person.TITLE) + " " + rechnungssteller.get(Rechnungssteller.FLD_NAME1) + " " + rechnungssteller.get(Rechnungssteller.FLD_NAME2)); } organization.addAddress(EhcCoreMapper.getEhcAddress(rechnungssteller.getAnschrift())); String phone = (String) rechnungssteller.get(Rechnungssteller.FLD_PHONE1); if (!StringTool.isNothing(phone)) { Telecoms telcoms = organization.getTelecoms(); telcoms.addPhone(phone, AddressUse.BUSINESS); } document.setCustodian(organization); // add ZSR to custodian organization id = DatatypesFactory.eINSTANCE.createII("2.16.756.5.30.1.105.1.1.2"); id.setExtension(getZsr(rezept)); clinicalDocument.getCustodian().getAssignedCustodian() .getRepresentedCustodianOrganization().getIds().add(id); // CDA body und rezeptzeilen Section section = CDAFactory.eINSTANCE.createSection(); clinicalDocument.addSection(section); CE prescriptionCode = DatatypesFactory.eINSTANCE.createCE(); prescriptionCode.setNullFlavor(NullFlavor.NA); CD translationCode = DatatypesFactory.eINSTANCE.createCD(); translationCode.setCodeSystem("2.16.756.5.30.1.105.2.2"); translationCode.setCode("VERORDNETEMEDIKAMENTE"); prescriptionCode.getTranslations().add(translationCode); section.setCode(prescriptionCode); ST title = DatatypesFactory.eINSTANCE.createST(); title.addText("Medikamente"); section.setTitle(title); StringBuilder sectionText = new StringBuilder(); addMedicationTextStart(sectionText); List<Prescription> prescriptions = rezept.getLines(); for (int idx = 0; idx < prescriptions.size(); idx++) { Prescription prescription = prescriptions.get(idx); SubstanceAdministration administration = CDAFactory.eINSTANCE.createSubstanceAdministration(); administration.setMoodCode(x_DocumentSubstanceMood.RQO); // pharmacode String pharmaCode = prescription.getArtikel().getPharmaCode(); II pharmacodeId = DatatypesFactory.eINSTANCE.createII("2.16.756.5.30.2.6.1"); pharmacodeId.setExtension(pharmaCode); administration.getIds().add(pharmacodeId); // atc code as material String atcCode = prescription.getArtikel().getATC_code(); ManufacturedProduct product = CDAFactory.eINSTANCE.createManufacturedProduct(); Material material = CDAFactory.eINSTANCE.createMaterial(); if (atcCode != null && !atcCode.isEmpty()) { material.setCode(DatatypesFactory.eINSTANCE.createCE(atcCode, "2.16.840.1.113883.6.73")); } else { material.setCode(DatatypesFactory.eINSTANCE.createCE()); } product.setManufacturedMaterial(material); Consumable consumable = CDAFactory.eINSTANCE.createConsumable(); consumable.setManufacturedProduct(product); administration.setConsumable(consumable); addDose(administration, prescription.getDosis()); addRemark(administration, prescription.getBemerkung()); addQuantity(administration, 1); // artikel, dosierung und bemerkung in text ED text = DatatypesFactory.eINSTANCE.createED(); TEL reference = DatatypesFactory.eINSTANCE.createTEL(); reference.setValue("#m" + idx); text.setReference(reference); // text.addText("<reference value=\"#m" + idx + "\" />"); administration.setText(text); // text of section addMedicationText(sectionText, prescription, idx); section.addSubstanceAdministration(administration); } addMedicationTextEnd(sectionText); section.createStrucDocText(sectionText.toString()); return document; } private static void addMedicationText(StringBuilder sectionText, Prescription prescription, int id){ // article sectionText.append("<tr>\n<td>\n"); sectionText.append("<content ID=\"m" + id + "\"> " + prescription.getArtikel().getLabel() + "</content>"); sectionText.append("\n</td>"); // dose sectionText.append("\n<td>\n"); sectionText.append(prescription.getDosis()); sectionText.append("\n</td>"); // valid date range sectionText.append("\n<td>\n"); String endDate = prescription.getEndDate(); if (endDate != null && !endDate.isEmpty()) { sectionText.append("Gültig bis " + endDate); } sectionText.append("\n</td>"); // remarks sectionText.append("\n<td>\n"); sectionText.append(prescription.getBemerkung()); sectionText.append("\n</td>\n</tr>"); } private static void addMedicationTextStart(StringBuilder sectionText){ sectionText.append("<table>\n<thead>\n<tr>\n") .append("<th>Präparat</th><th>Dosis</th><th>Gültigkeit</th><th>Verabreichung</th>") .append("\n</tr>\n</thead>\n").append("<tbody>\n"); } private static void addMedicationTextEnd(StringBuilder sectionText){ sectionText.append("\n</tbody>\n</table>\n"); } private static void addQuantity(SubstanceAdministration administration, int quantity){ if (administration != null) { EntryRelationship relationship = CDAFactory.eINSTANCE.createEntryRelationship(); relationship.setTypeCode(x_ActRelationshipEntryRelationship.REFR); relationship.setInversionInd(false); Supply supply = CDAFactory.eINSTANCE.createSupply(); supply.setClassCode(ActClassSupply.SPLY); supply.setMoodCode(x_DocumentSubstanceMood.INT); PQ quantityPQ = DatatypesFactory.eINSTANCE.createPQ(); quantityPQ.setValue(Double.valueOf(quantity)); supply.setQuantity(quantityPQ); relationship.setSupply(supply); administration.getEntryRelationships().add(relationship); } } private static void addRemark(SubstanceAdministration administration, String bemerkung){ if (administration != null && bemerkung != null && !bemerkung.isEmpty()) { EntryRelationship relationship = CDAFactory.eINSTANCE.createEntryRelationship(); relationship.setTypeCode(x_ActRelationshipEntryRelationship.SPRT); Act act = CDAFactory.eINSTANCE.createAct(); act.setClassCode(x_ActClassDocumentEntryAct.INFRM); act.setMoodCode(x_DocumentActMood.RQO); CD code = DatatypesFactory.eINSTANCE.createCD(); code.setNullFlavor(NullFlavor.NA); ED text = DatatypesFactory.eINSTANCE.createED(); text.addText(bemerkung); code.setOriginalText(text); act.setCode(code); relationship.setAct(act); administration.getEntryRelationships().add(relationship); } } private static void addDose(SubstanceAdministration administration, String dosis){ ArrayList<Float> doseFloats = Prescription.getDoseAsFloats(dosis); if (!doseFloats.isEmpty()) { if (doseFloats.size() == 1) { // assume per day if (doseFloats.get(0) > 0) { addEffectiveTime(administration, "", "1", "d"); addDoseQuantity(administration, doseFloats.get(0), "1"); } } else { // morning, midday, evening, night for (int i = 0; i < doseFloats.size(); i++) { if (doseFloats.get(i) > 0) { SubstanceAdministration doseAdministration = addDoseAdministration(administration); if (i < 4) { addEffectiveTime(doseAdministration, DOSE_TIME[i], "1", "d"); } addDoseQuantity(doseAdministration, doseFloats.get(i), "1"); } } } } // default do not encode ... info has to be in remark or text form } private static SubstanceAdministration addDoseAdministration( SubstanceAdministration administration){ if (administration != null) { EntryRelationship relationship = CDAFactory.eINSTANCE.createEntryRelationship(); relationship.setTypeCode(x_ActRelationshipEntryRelationship.COMP); SubstanceAdministration ret = CDAFactory.eINSTANCE.createSubstanceAdministration(); ret.setMoodCode(x_DocumentSubstanceMood.RQO); ManufacturedProduct product = CDAFactory.eINSTANCE.createManufacturedProduct(); Material material = CDAFactory.eINSTANCE.createMaterial(); material.setCode(DatatypesFactory.eINSTANCE.createCE()); product.setManufacturedMaterial(material); Consumable consumable = CDAFactory.eINSTANCE.createConsumable(); consumable.setManufacturedProduct(product); ret.setConsumable(consumable); relationship.setSubstanceAdministration(ret); administration.getEntryRelationships().add(relationship); return ret; } return null; } private static void addDoseQuantity(SubstanceAdministration administration, Float value, String unit){ if (administration != null) { IVL_PQ doseQuantity = DatatypesFactory.eINSTANCE.createIVL_PQ(); PQ quantity = DatatypesFactory.eINSTANCE.createPQ(); quantity.setValue(Double.valueOf(value)); quantity.setUnit(unit); doseQuantity.setCenter(quantity); administration.setDoseQuantity(doseQuantity); } } private static void addEffectiveTime(SubstanceAdministration administration, String time, String periodValue, String periodUnit){ if (administration != null) { PIVL_TS effectiveTime = DatatypesFactory.eINSTANCE.createPIVL_TS(); effectiveTime.setOperator(SetOperator.A); IVL_TS phase = DatatypesFactory.eINSTANCE.createIVL_TS(); phase.setValue(time); effectiveTime.setPhase(phase); PQ period = DatatypesFactory.eINSTANCE.createPQ(); period.setValue(Double.parseDouble(periodValue)); period.setUnit(periodUnit); effectiveTime.setPeriod(period); administration.getEffectiveTimes().add(effectiveTime); } } public static ByteArrayOutputStream getPrescriptionPdf(ByteArrayOutputStream cdaOutput){ IFormattedOutput foImpl = foFactory.getFormattedOutputImplementation(ObjectType.XMLSTREAM, OutputType.PDF); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { foImpl.transform(new ByteArrayInputStream(cdaOutput.toByteArray()), DocboxService.class.getResourceAsStream("/rsc/xsl/prescription.xsl"), out); } catch (Exception e) { logger.error("Could not create prescription PDF" + e); } return out; } public static ByteArrayOutputStream getPrescriptionPdf(AbstractCdaCh<?> cdaPrescription) throws Exception{ ByteArrayOutputStream cdaOutput = new ByteArrayOutputStream(); CDAUtil.save(cdaPrescription.getDocRoot().getClinicalDocument(), cdaOutput); return getPrescriptionPdf(cdaOutput); } public static String sendPrescription(InputStream xmlFile, InputStream pdfFile){ SendClinicalDocumentClient send = new SendClinicalDocumentClient(); boolean success = send.sendClinicalDocument(xmlFile, pdfFile); String message = send.getMessage(); if (!success) { message = "FAILED " + message; } return message; } private static String getRezeptId(Rezept rezept){ Date now = new Date(); StringBuilder sb = new StringBuilder(); sb.append("202"); sb.append(getIdZsr(rezept)); sb.append(new SimpleDateFormat("yyyy").format(now)); sb.append(new SimpleDateFormat("MM").format(now)); sb.append(new SimpleDateFormat("dd").format(now)); sb.append(new SimpleDateFormat("HH").format(now)); sb.append(new SimpleDateFormat("mm").format(now)); sb.append(new SimpleDateFormat("ss").format(now)); // Milliseconds ... String millis = new SimpleDateFormat("SS").format(now); if (millis.length() == 1) { sb.append("0").append(millis); } else if (millis.length() == 2) { sb.append(millis); } else if (millis.length() == 3) { sb.append(millis.substring(1)); } else { sb.append("00"); } String checkString = sb.toString(); try { int checkSum = 0; for (int i = 0; i < checkString.length(); i++) { checkSum += Integer.parseInt(checkString.substring(i, i + 1)); } sb.append(String.valueOf(checkSum % 10)); } catch (NumberFormatException ne) { logger.error("Could not generate checksum for [" + checkString + "]"); throw ne; } return sb.toString(); } private static String getIdZsr(Rezept rezept){ String zsr = getZsr(rezept); if (zsr != null && !zsr.isEmpty()) { if (zsr.length() >= 6) { return zsr.substring(zsr.length() - 6, zsr.length()); } } throw new IllegalStateException("Keine ZSR gefunden"); } private static String getZsr(Rezept rezept){ Mandant mandant = rezept.getMandant(); Rechnungssteller rechnungssteller = mandant.getRechnungssteller(); String zsr = rechnungssteller.getXid(DOMAIN_KSK); if (zsr != null && !zsr.isEmpty() && zsr.length() >= 6) { return zsr.replaceAll("\\.", ""); } zsr = rechnungssteller.getInfoString("KSK"); if (zsr != null && !zsr.isEmpty() && zsr.length() >= 6) { return zsr.replaceAll("\\.", ""); } zsr = mandant.getXid(DOMAIN_KSK); if (zsr != null && !zsr.isEmpty() && zsr.length() >= 6) { return zsr.replaceAll("\\.", ""); } zsr = mandant.getInfoString("KSK"); if (zsr != null && !zsr.isEmpty() && zsr.length() >= 6) { return zsr.replaceAll("\\.", ""); } throw new IllegalStateException("Keine ZSR gefunden"); } }