001package org.hl7.fhir.convertors.misc.argonaut; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032 033import org.apache.commons.io.IOUtils; 034import org.fhir.ucum.UcumEssenceService; 035import org.fhir.ucum.UcumService; 036import org.hl7.fhir.convertors.misc.CDAUtilities; 037import org.hl7.fhir.convertors.misc.ccda.CcdaExtensions; 038import org.hl7.fhir.convertors.misc.Convert; 039import org.hl7.fhir.convertors.misc.ConverterBase; 040import org.hl7.fhir.dstu3.context.SimpleWorkerContext; 041import org.hl7.fhir.dstu3.formats.IParser.OutputStyle; 042import org.hl7.fhir.dstu3.formats.JsonParser; 043import org.hl7.fhir.dstu3.formats.XmlParser; 044import org.hl7.fhir.dstu3.model.*; 045import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceReactionComponent; 046import org.hl7.fhir.dstu3.model.Condition.ConditionVerificationStatus; 047import org.hl7.fhir.dstu3.model.DocumentReference.DocumentReferenceContentComponent; 048import org.hl7.fhir.dstu3.model.DocumentReference.DocumentReferenceContextComponent; 049import org.hl7.fhir.dstu3.model.Encounter.EncounterHospitalizationComponent; 050import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus; 051import org.hl7.fhir.dstu3.model.Enumerations.DocumentReferenceStatus; 052import org.hl7.fhir.dstu3.model.Immunization.ImmunizationExplanationComponent; 053import org.hl7.fhir.dstu3.model.Immunization.ImmunizationStatus; 054import org.hl7.fhir.dstu3.model.ListResource.ListMode; 055import org.hl7.fhir.dstu3.model.ListResource.ListStatus; 056import org.hl7.fhir.dstu3.model.MedicationStatement.MedicationStatementStatus; 057import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus; 058import org.hl7.fhir.dstu3.model.Observation.ObservationRelationshipType; 059import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; 060import org.hl7.fhir.dstu3.model.Procedure.ProcedurePerformerComponent; 061import org.hl7.fhir.dstu3.model.Procedure.ProcedureStatus; 062import org.hl7.fhir.dstu3.utils.NarrativeGenerator; 063import org.hl7.fhir.dstu3.utils.ResourceUtilities; 064import org.hl7.fhir.utilities.Utilities; 065import org.hl7.fhir.utilities.ZipGenerator; 066import org.hl7.fhir.utilities.validation.ValidationMessage; 067import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 068import org.hl7.fhir.utilities.xhtml.NodeType; 069import org.hl7.fhir.utilities.xhtml.XhtmlNode; 070import org.hl7.fhir.utilities.xml.XMLUtil; 071import org.w3c.dom.Element; 072 073import java.io.ByteArrayOutputStream; 074import java.io.File; 075import java.io.FileInputStream; 076import java.io.IOException; 077import java.util.*; 078 079public class ArgonautConverter extends ConverterBase { 080 // public final static String DEF_TS_SERVER = "http://fhir-dev.healthintersections.com.au/open"; 081 public final static String DEV_TS_SERVER = "http://local.fhir.org:960/open"; 082 public static final String UCUM_PATH = "c:\\work\\org.hl7.fhir\\build\\implementations\\java\\org.hl7.fhir.convertors\\samples\\ucum-essence.xml"; 083 public static final String SRC_PATH = "c:\\work\\org.hl7.fhir\\build\\publish\\"; 084 private static final String DEFAULT_ID_SPACE = "urn:uuid:e8e06b15-0f74-4b8e-b5e2-609dae7119dc"; 085 086 private static final boolean WANT_SAVE = true; 087 private static final boolean WANT_VALIDATE = false; 088 private final UcumService ucumSvc; 089 // private ValidationEngine validator; 090 private final SimpleWorkerContext context; 091 private final Map<String, Map<String, Integer>> sections = new HashMap<>(); 092 private final Map<String, Practitioner> practitionerCache = new HashMap<>(); 093 private final Set<String> oids = new HashSet<>(); 094 private final Map<String, ZipGenerator> zipsX = new HashMap<>(); 095 private final Map<String, ZipGenerator> zipsJ = new HashMap<>(); 096 private final Map<String, Stats> stats = new HashMap<>(); 097 public int perfCount; 098 int errors = 0; 099 int warnings = 0; 100 Map<String, Integer> procCodes = new HashMap<>(); 101 Map<String, Integer> condCodes = new HashMap<>(); 102 Map<String, Integer> allergyCodes = new HashMap<>(); 103 private String destFolder; 104 private ZipGenerator zipJ; 105 private ZipGenerator zipX; 106 107 public ArgonautConverter(UcumService ucumSvc, String path) throws Exception { 108 super(); 109 this.ucumSvc = ucumSvc; 110 context = SimpleWorkerContext.fromPack(path); 111// validator = new ValidationEngine(); 112// validator.readDefinitions(path); 113// validator.setAnyExtensionsAllowed(true); 114 } 115 116 public static void main(String[] args) { 117 try { 118 ArgonautConverter c = new ArgonautConverter(new UcumEssenceService(UCUM_PATH), Utilities.path(SRC_PATH, "validation.xml.zip")); 119 c.destFolder = "C:\\work\\com.healthintersections.fhir\\argonaut\\fhir"; 120 c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\file_emergency", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("EMER")); 121 c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\file_ed", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("IMP")); 122 c.convert("C:\\work\\com.healthintersections.fhir\\argonaut\\cda\\fileX", new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB")); 123 c.printSectionSummaries(); 124 c.closeZips(); 125 System.out.println("All done. " + c.getErrors() + " errors, " + c.getWarnings() + " warnings"); 126 } catch (Exception e) { 127 e.printStackTrace(); 128 } 129 } 130 131 public int getErrors() { 132 return errors; 133 } 134 135 public int getWarnings() { 136 return warnings; 137 } 138 139 public void convert(String sourceFolder, Coding clss) throws Exception { 140 File source = new File(sourceFolder); 141 for (String f : source.list()) { 142 convert(sourceFolder, f, clss); 143 } 144 } 145 146 private void closeZips() throws Exception { 147 for (ZipGenerator z : zipsJ.values()) 148 z.close(); 149 for (ZipGenerator z : zipsX.values()) 150 z.close(); 151 } 152 153 public void printSectionSummaries() { 154 System.out.println("Statistics:"); 155 for (String n : sorted(stats.keySet())) { 156 Stats s = stats.get(n); 157 System.out.println(" " + n + ": generated " + s.getInstances() + ", errors " + s.getErrors() + ", warnings " + s.getWarnings()); 158 } 159 160 System.out.println("OIDs:"); 161 for (String n : sorted(oids)) 162 System.out.println(" " + n); 163 164 for (String n : sections.keySet()) { 165 System.out.println(n + " Analysis"); 166 Map<String, Integer> s = sections.get(n); 167 for (String p : sorted(s.keySet())) { 168 System.out.println(" " + p + ": " + s.get(p)); 169 } 170 } 171 172 dumpCodes(); 173 } 174 175 private List<String> sorted(Set<String> keys) { 176 List<String> names = new ArrayList<>(); 177 names.addAll(keys); 178 Collections.sort(names); 179 return names; 180 } 181 182 private void convert(String sourceFolder, String filename, Coding clss) throws IOException { 183 if (new File(Utilities.path(sourceFolder, filename)).length() == 0) 184 return; 185 186 CDAUtilities cda; 187 try { 188 System.out.println("Process " + Utilities.path(sourceFolder, filename)); 189 cda = new CDAUtilities(new FileInputStream(Utilities.path(sourceFolder, filename))); 190 zipJ = new ZipGenerator(Utilities.path(destFolder, "json/doc", Utilities.changeFileExt(filename, ".json.zip"))); 191 zipX = new ZipGenerator(Utilities.path(destFolder, "xml/doc", Utilities.changeFileExt(filename, ".xml.zip"))); 192 Element doc = cda.getElement(); 193 Convert convert = new Convert(cda, ucumSvc, "-0400"); 194 convert.setGenerateMissingExtensions(true); 195 Context context = new Context(); 196 context.setBaseId(Utilities.changeFileExt(filename, "")); 197 context.setEncClass(clss); 198 makeSubject(cda, convert, doc, context, context.getBaseId() + "-patient"); 199 makeAuthor(cda, convert, doc, context, context.getBaseId() + "-author"); 200 makeEncounter(cda, convert, doc, context, context.getBaseId() + "-encounter"); 201 Element body = cda.getDescendent(doc, "component/structuredBody"); 202 for (Element c : cda.getChildren(body, "component")) { 203 processSection(cda, convert, context, cda.getChild(c, "section")); 204 } 205 oids.addAll(convert.getOids()); 206 saveResource(context.getEncounter()); 207 makeBinary(sourceFolder, filename, context); 208 makeDocumentReference(cda, convert, doc, context); 209 zipJ.close(); 210 zipX.close(); 211 } catch (Exception e) { 212 throw new Error("Unable to process " + Utilities.path(sourceFolder, filename) + ": " + e.getMessage(), e); 213 } 214 } 215 216 private void processSection(CDAUtilities cda, Convert convert, Context context, Element section) throws Exception { 217 checkNoSubject(cda, section, "Section"); 218 219 // this we do by templateId 220 if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.11") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.5.1")) 221 processProblemsSection(cda, convert, section, context); 222 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.12") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.7.1")) 223 processProcedureSection(cda, convert, section, context); 224 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.3") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.22.1")) 225 processEncountersSection(cda, convert, section, context); 226 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.6.1")) 227 processAllergiesSection(cda, convert, section, context); 228 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.2.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.6")) 229 processImmunizationsSection(cda, convert, section, context); 230 else if (cda.hasTemplateId(section, "1.3.6.1.4.1.19376.1.5.3.1.3.1")) 231 processReasonForEncounter(cda, convert, section, context); 232 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.3.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.14")) 233 processResultsSection(cda, convert, section, context); 234 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.4.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.16")) 235 processVitalSignsSection(cda, convert, section, context); 236 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.1.1") || cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.8")) 237 processMedicationsSection(cda, convert, section, context); 238 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.22.2.17") || cda.hasTemplateId(section, "2.16.840.1.113883.3.88.11.83.126")) 239 processSocialHistorySection(cda, convert, section, context); 240 else if (cda.hasTemplateId(section, "2.16.840.1.113883.10.20.1.9")) 241 scanSection("Payers", section); 242 else 243 throw new Exception("Unprocessed section " + cda.getChild(section, "title").getTextContent()); 244 } 245 246 private void checkNoSubject(CDAUtilities cda, Element act, String path) throws Exception { 247 if (cda.getChild(act, "subject") != null) 248 throw new Exception("The conversion program cannot accept a subject at the location " + path); 249 } 250 251 private void scanSection(String name, Element child) { 252 Map<String, Integer> section; 253 if (sections.containsKey(name)) 254 section = sections.get(name); 255 else { 256 section = new HashMap<>(); 257 sections.put(name, section); 258 } 259 iterateChildren(section, "/", child); 260 } 261 262 private void iterateChildren(Map<String, Integer> section, String path, Element element) { 263 Element child = XMLUtil.getFirstChild(element); 264 while (child != null) { 265 String pathC = path + child.getNodeName() + attributes(child); 266 if (section.containsKey(pathC)) 267 section.put(pathC, section.get(pathC) + 1); 268 else 269 section.put(pathC, 1); 270 iterateChildren(section, pathC + "/", child); 271 child = XMLUtil.getNextSibling(child); 272 } 273 } 274 275 private String attributes(Element child) { 276 String s = ","; 277 if (child.hasAttribute("inversionInd")) 278 s += "inversionInd:" + child.getAttribute("inversionInd") + ","; 279 if (child.hasAttribute("negationInd")) 280 s += "negationInd:" + child.getAttribute("negationInd") + ","; 281 if (child.hasAttribute("nullFlavor")) 282 s += "nullFlavor:" + child.getAttribute("nullFlavor") + ","; 283 if (child.hasAttribute("xsi:type")) 284 s += "type:" + child.getAttribute("xsi:type") + ","; 285 s = s.substring(0, s.length() - 1); 286 287 if (child.getNodeName().equals("statusCode")) 288 return "[code:" + child.getAttribute("code") + "]"; 289 if (child.getNodeName().equals("temnplateId")) 290 return "[id:" + child.getAttribute("root") + "]"; 291 else if (child.hasAttribute("moodCode")) 292 return "[" + child.getAttribute("classCode") + "," + child.getAttribute("moodCode") + s + "]"; 293 else if (child.hasAttribute("classCode")) 294 return "[" + child.getAttribute("classCode") + s + "]"; 295 else if (child.hasAttribute("typeCode")) 296 return "[" + child.getAttribute("typeCode") + s + "]"; 297 else if (Utilities.noString(s)) 298 return ""; 299 else 300 return "[" + s.substring(1) + "]"; 301 } 302 303 private void saveResource(Resource resource) throws Exception { 304 saveResource(resource, null); 305 } 306 307 private void saveResource(Resource resource, String extraType) throws Exception { 308 if (!WANT_SAVE) 309 return; 310 311 DomainResource dr = null; 312 if (resource instanceof DomainResource) { 313 dr = (DomainResource) resource; 314 if (!dr.hasText()) { 315 NarrativeGenerator generator = new NarrativeGenerator("", "", context); 316 generator.generate(dr); 317 } 318 } 319 XmlParser xparser = new XmlParser(); 320 xparser.setOutputStyle(OutputStyle.PRETTY); 321 JsonParser jparser = new JsonParser(); 322 jparser.setOutputStyle(OutputStyle.PRETTY); 323 324 ByteArrayOutputStream ba = new ByteArrayOutputStream(); 325 xparser.compose(ba, resource); 326 ba.close(); 327 byte[] srcX = ba.toByteArray(); 328 ba = new ByteArrayOutputStream(); 329 jparser.compose(ba, resource); 330 ba.close(); 331 byte[] srcJ = ba.toByteArray(); 332 333 String rn = resource.getResourceType().toString(); 334 if (extraType != null) 335 rn = rn + extraType; 336 zipX.addBytes(resource.getId() + ".xml", srcX, false); 337 zipJ.addBytes(resource.getId() + ".json", srcJ, false); 338 if (!zipsX.containsKey(rn)) { 339 zipsX.put(rn, new ZipGenerator(Utilities.path(destFolder, "xml/type", rn + ".xml.zip"))); 340 zipsJ.put(rn, new ZipGenerator(Utilities.path(destFolder, "json/type", rn + ".json.zip"))); 341 stats.put(rn, new Stats()); 342 } 343 344 zipsJ.get(rn).addBytes(resource.getId() + ".json", srcJ, false); 345 zipsX.get(rn).addBytes(resource.getId() + ".xml", srcX, false); 346 Stats ss = stats.get(rn); 347 ss.setInstances(ss.getInstances() + 1); 348 349 String profile = resource.getUserString("profile"); 350 validate(srcX, profile, resource, ss); 351 } 352 353 private void validate(byte[] src, String url, Resource resource, Stats stats) throws Exception { 354 if (!WANT_VALIDATE) 355 return; 356 if (url == null) 357 url = "http://hl7.org/fhir/StructureDefinition/" + resource.getResourceType().toString(); 358 StructureDefinition def = context.fetchResource(StructureDefinition.class, url); 359 if (def == null) 360 throw new Exception("Unable to find Structure Definition " + url); 361 362// validator.reset(); 363// validator.setProfile(def); 364// validator.setSource(src); 365// validator.process(); 366 List<ValidationMessage> msgs = null; // validator.getOutputs(); 367 boolean ok = false; 368 boolean first = true; 369 for (ValidationMessage m : msgs) { 370 if (m.getLevel() == IssueSeverity.ERROR && !msgOk(m.getMessage())) { 371 if (first) { 372 System.out.println(" validate " + resource.getId() + ".xml against " + url); 373 first = false; 374 } 375 System.out.println(" " + m.getLevel().toCode() + ": " + m.getMessage() + " @ " + m.getLocation()); 376 if (m.getLevel() == IssueSeverity.WARNING) { 377 stats.setWarnings(stats.getWarnings() + 1); 378 warnings++; 379 } 380 if (m.getLevel() == IssueSeverity.ERROR || m.getLevel() == IssueSeverity.FATAL) { 381 stats.setErrors(stats.getErrors() + 1); 382 errors++; 383 } 384 } 385 386 ok = ok && !(m.getLevel() == IssueSeverity.ERROR || m.getLevel() == IssueSeverity.FATAL); 387 } 388 } 389 390 private boolean msgOk(String message) { 391 return message.equals("Invalid Resource target type. Found Observation, but expected one of (DiagnosticReport)"); 392 } 393 394 private void checkGenerateIdentifier(List<Identifier> ids, DomainResource resource) { 395 if (ids.isEmpty()) 396 ids.add(new Identifier().setSystem(DEFAULT_ID_SPACE).setValue(resource.getClass().getName().toLowerCase() + "-" + resource.getId())); 397 } 398 399 private void makeSubject(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception { 400 Element rt = cda.getChild(doc, "recordTarget"); 401 scanSection("Patient", rt); 402 Element pr = cda.getChild(rt, "patientRole"); 403 Element p = cda.getChild(pr, "patient"); 404 405 Patient pat = new Patient(); 406 pat.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/patient-daf-dafpatient"); 407 StringBuilder b = new StringBuilder(); 408 409 pat.setId(id); 410 for (Element e : cda.getChildren(p, "name")) { 411 HumanName name = convert.makeNameFromEN(e); 412 pat.getName().add(name); 413 b.append(NarrativeGenerator.displayHumanName(name)); 414 b.append(" "); 415 } 416 b.append("("); 417 for (Element e : cda.getChildren(pr, "id")) { 418 Identifier identifier = convert.makeIdentifierFromII(e); 419 pat.getIdentifier().add(identifier); 420 b.append(identifier.getValue()); 421 b.append(", "); 422 } 423 424 for (Element e : cda.getChildren(pr, "addr")) 425 pat.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e))); 426 for (Element e : cda.getChildren(pr, "telecom")) 427 pat.getTelecom().add(convert.makeContactFromTEL(e)); 428 pat.setGender(convert.makeGenderFromCD(cda.getChild(p, "administrativeGenderCode"))); 429 b.append(pat.getGender().getDisplay()); 430 b.append(", "); 431 pat.setBirthDateElement(convert.makeDateFromTS(cda.getChild(p, "birthTime"))); 432 b.append("DOB: "); 433 b.append(pat.getBirthDateElement().toHumanDisplay()); 434 b.append(")"); 435 pat.setMaritalStatus(convert.makeCodeableConceptFromCD(cda.getChild(p, "maritalStatusCode"))); 436 437 pat.addExtension(Factory.newExtension(CcdaExtensions.DAF_NAME_RACE, convert.makeCodeableConceptFromCD(cda.getChild(p, "raceCode")), false)); 438 pat.addExtension(Factory.newExtension(CcdaExtensions.DAF_NAME_ETHNICITY, convert.makeCodeableConceptFromCD(cda.getChild(p, "ethnicGroupCode")), false)); 439 440 pat.addExtension(Factory.newExtension(CcdaExtensions.NAME_RELIGION, convert.makeCodeableConceptFromCD(cda.getChild(p, "religiousAffiliationCode")), false)); 441 pat.addExtension(Factory.newExtension(CcdaExtensions.NAME_BIRTHPLACE, convert.makeAddressFromAD(cda.getChild(p, new String[]{"birthplace", "place", "addr"})), false)); 442 443 Element g = cda.getChild(p, "guardian"); 444 if (g != null) { 445 Patient.ContactComponent guardian = new Patient.ContactComponent(); 446 pat.getContact().add(guardian); 447 guardian.getRelationship().add(Factory.newCodeableConcept("GUARD", "urn:oid:2.16.840.1.113883.5.110", "guardian")); 448 for (Element e : cda.getChildren(g, "addr")) 449 if (guardian.getAddress() == null) 450 guardian.setAddress(makeDefaultAddress(convert.makeAddressFromAD(e))); 451 for (Element e : cda.getChildren(g, "telecom")) 452 guardian.getTelecom().add(convert.makeContactFromTEL(e)); 453 g = cda.getChild(g, "guardianPerson"); 454 for (Element e : cda.getChildren(g, "name")) 455 if (guardian.getName() == null) 456 guardian.setName(convert.makeNameFromEN(e)); 457 } 458 459 Element l = cda.getChild(p, "languageCommunication"); 460 CodeableConcept cc = new CodeableConcept(); 461 Coding c = new Coding(); 462 c.setSystem(ResourceUtilities.FHIR_LANGUAGE); 463 c.setCode(patchLanguage(cda.getChild(l, "languageCode").getAttribute("code"))); 464 cc.getCoding().add(c); 465 pat.addCommunication().setLanguage(cc); 466 467 Element prv = cda.getChild(pr, "providerOrganization"); 468 if (prv != null) 469 pat.setManagingOrganization(new Reference().setReference("Organization/" + processOrganization(prv, cda, convert, context).getId())); 470 471 context.setSubjectRef(new Reference().setDisplay(b.toString()).setReference("Patient/" + pat.getId())); 472 saveResource(pat); 473 } 474 475 private Organization processOrganization(Element oo, CDAUtilities cda, Convert convert, Context context) throws Exception { 476 Organization org = new Organization(); 477 org.setId(context.getBaseId() + "-organization-" + context.getOrgId()); 478 org.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/org-daf-daforganization"); 479 context.setOrgId(context.getOrgId() + 1); 480 for (Element e : cda.getChildren(oo, "id")) 481 org.getIdentifier().add(convert.makeIdentifierFromII(e)); 482 for (Element e : cda.getChildren(oo, "addr")) 483 org.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e))); 484 for (Element e : cda.getChildren(oo, "telecom")) { 485 ContactPoint cp = convert.makeContactFromTEL(e); 486 if (Utilities.noString(cp.getValue())) 487 cp.setValue("1 (555) 555 5555"); 488 org.getTelecom().add(cp); 489 } 490 for (Element e : cda.getChildren(oo, "name")) 491 org.setName(e.getTextContent()); 492 saveResource(org); 493 return org; 494 } 495 496 private Address makeDefaultAddress(Address ad) { 497 if (ad == null || ad.isEmpty()) { 498 ad = new Address(); 499 ad.addLine("21 Doar road"); 500 ad.setCity("Erewhon"); 501 ad.setState("CA"); 502 ad.setPostalCode("31233"); 503 } 504 return ad; 505 } 506 507 private String patchLanguage(String lang) { 508 if (lang.equals("spa")) 509 return "es"; 510 if (lang.equals("eng")) 511 return "en"; 512 return lang; 513 } 514 515 ///legalAuthenticator/assignedEntity: 2979 516 ///legalAuthenticator/assignedEntity/addr: 2979 517 ///legalAuthenticator/assignedEntity/assignedPerson: 2979 518 ///legalAuthenticator/assignedEntity/id: 2979 519 ///legalAuthenticator/assignedEntity/representedOrganization: 2979 520 ///legalAuthenticator/assignedEntity/representedOrganization/addr: 2979 521 ///legalAuthenticator/assignedEntity/representedOrganization/id: 2979 522 ///legalAuthenticator/assignedEntity/representedOrganization/name: 2979 523 ///legalAuthenticator/assignedEntity/representedOrganization/telecom: 2979 524 ///legalAuthenticator/assignedEntity/telecom: 2979 525 526 private Practitioner makeAuthor(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception { 527 Element a = cda.getChild(doc, "author"); 528 scanSection("Author", a); 529 Practitioner author = processPerformer(cda, convert, context, a, "assignedAuthor", "assignedPerson"); 530 context.setAuthorRef(new Reference().setDisplay(author.getUserString("display")).setReference("Practitioner/" + author.getId())); 531 return author; 532 } 533 534 private Practitioner makePerformer(CDAUtilities cda, Convert convert, Context context, Element eperf, String roleName, String entityName) throws Exception { 535 Element ae = cda.getChild(eperf, roleName); 536 Element ap = cda.getChild(ae, entityName); 537 538 StringBuilder b = new StringBuilder(); 539 540 Practitioner perf = new Practitioner(); 541 perf.setId("performer-" + perfCount); 542 perf.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/pract-daf-dafpract"); 543 perfCount++; 544 for (Element e : cda.getChildren(ae, "id")) { 545 Identifier id = convert.makeIdentifierFromII(e); 546 perf.getIdentifier().add(id); 547 } 548 549 for (Element e : cda.getChildren(ap, "name")) { 550 HumanName name = convert.makeNameFromEN(e); 551 perf.addName(name); 552 b.append(NarrativeGenerator.displayHumanName(name)); 553 b.append(" "); 554 } 555 for (Element e : cda.getChildren(ae, "addr")) 556 perf.getAddress().add(makeDefaultAddress(convert.makeAddressFromAD(e))); 557 boolean first = true; 558 for (Element e : cda.getChildren(ae, "telecom")) { 559 ContactPoint contact = convert.makeContactFromTEL(e); 560 perf.getTelecom().add(contact); 561 if (!Utilities.noString(contact.getValue())) { 562 if (first) { 563 b.append("("); 564 first = false; 565 } else 566 b.append(" "); 567 b.append(NarrativeGenerator.displayContactPoint(contact)); 568 } 569 } 570 if (!first) 571 b.append(")"); 572 573// Element e = cda.getChild(ae, "representedOrganization"); 574// if (e != null) 575// perf.addRole().setOrganization(new Reference().setReference("Organization/"+processOrganization(e, cda, convert, context).getId())); 576 perf.setUserData("display", b.toString()); 577 return perf; 578 } 579 580 ///serviceEvent/performer/functionCode: 9036 581 private Encounter makeEncounter(CDAUtilities cda, Convert convert, Element doc, Context context, String id) throws Exception { 582 Element co = cda.getChild(doc, "componentOf"); 583 Element ee = cda.getChild(co, "encompassingEncounter"); 584 scanSection("Encounter", co); 585 Element of = cda.getChild(doc, "documentationOf"); 586 Element se = cda.getChild(of, "serviceEvent"); 587 scanSection("Encounter", of); 588 589 Encounter enc = new Encounter(); 590 enc.setId(id); 591 enc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/encounter-daf-dafencounter"); 592 context.setEncounter(enc); 593 enc.setSubject(context.getSubjectRef()); 594 595 for (Element e : cda.getChildren(ee, "id")) 596 enc.getIdentifier().add(convert.makeIdentifierFromII(e)); 597 checkGenerateIdentifier(enc.getIdentifier(), enc); 598 599 Period p1 = convert.makePeriodFromIVL(cda.getChild(ee, "effectiveTime")); 600 // Period p2 = convert.makePeriodFromIVL(cda.getChild(se, "effectiveTime")); // well, what is this? 601 // if (!Base.compareDeep(p1, p2, false)) 602 // throw new Error("episode time mismatch: "+NarrativeGenerator.displayPeriod(p1)+" & "+NarrativeGenerator.displayPeriod(p2)); 603 enc.setPeriod(p1); 604 if (p1.hasEnd()) 605 enc.setStatus(EncounterStatus.FINISHED); 606 else 607 enc.setStatus(EncounterStatus.INPROGRESS); 608 enc.setClass_(context.getEncClass()); 609 610 Element dd = cda.getChild(ee, "dischargeDispositionCode"); 611 if (dd != null) { 612 enc.setHospitalization(new EncounterHospitalizationComponent()); 613 enc.getHospitalization().setDischargeDisposition(convert.makeCodeableConceptFromCD(dd)); 614 } 615 for (Element e : cda.getChildren(se, "performer")) { 616 Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson"); 617 Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")); 618 if (ref != null) 619 enc.addParticipant().setIndividual(ref); 620 } 621 return enc; 622 } 623 624 private Practitioner processPerformer(CDAUtilities cda, Convert convert, Context context, Element e, String roleName, String entityName) throws Exception { 625 Practitioner perf = makePerformer(cda, convert, context, e, roleName, entityName); 626 if (perf == null) 627 return null; 628 629 Reference ref = null; 630 for (Identifier identifier : perf.getIdentifier()) { 631 String key = keyFor(identifier); 632 if (practitionerCache.containsKey(key)) 633 return practitionerCache.get(key); 634 } 635 636 saveResource(perf); 637 for (Identifier identifier : perf.getIdentifier()) { 638 String key = "Practitioner-" + keyFor(identifier); 639 practitionerCache.put(key, perf); 640 } 641 return perf; 642 } 643 644 private String keyFor(Identifier identifier) { 645 return identifier.getSystem() + "||" + identifier.getValue(); 646 } 647 648 private void buildNarrative(DomainResource resource, Element child) { 649 if (!Utilities.noString(child.getTextContent())) { 650 XhtmlNode div = new XhtmlNode(NodeType.Element, "div"); 651 String s = child.getTextContent().trim(); 652 if (Utilities.noString(s)) 653 div.addText("No Narrative provided in the source CDA document"); 654 else 655 div.addText(s); 656 resource.setText(new Narrative().setStatus(NarrativeStatus.ADDITIONAL).setDiv(div)); 657 } 658 } 659 660 private void processProcedureSection(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception { 661 scanSection("Procedures", sect); 662 ListResource list = new ListResource(); 663 list.setId(context.getBaseId() + "-list-procedures"); 664 // list.setUserData("profile", "") none? 665 list.setSubject(context.getSubjectRef()); 666 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sect, "code")), null)); 667 list.setTitle(cda.getChild(sect, "title").getTextContent()); 668 list.setStatus(ListStatus.CURRENT); 669 list.setMode(ListMode.SNAPSHOT); 670 list.setDateElement(context.getNow()); 671 list.setSource(context.getAuthorRef()); 672 buildNarrative(list, cda.getChild(sect, "text")); 673 674 int i = 0; 675 for (Element c : cda.getChildren(sect, "entry")) { 676 Element p = cda.getChild(c, "procedure"); 677 Procedure proc = new Procedure(); 678 proc.setId(context.getBaseId() + "-procedure-" + i); 679 proc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/procedure-daf-dafprocedure"); 680 i++; 681 proc.setSubject(context.getSubjectRef()); 682 proc.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 683 list.addEntry().setItem(new Reference().setReference("Procedure/" + proc.getId())); 684 proc.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(p, "code")), null)); 685 recordProcedureCode(proc.getCode()); 686 for (Element e : cda.getChildren(p, "id")) 687 proc.getIdentifier().add(convert.makeIdentifierFromII(e)); 688 689 proc.setStatus(determineProcedureStatus(cda.getChild(p, "statusCode"))); 690 buildNarrative(proc, cda.getChild(p, "text")); 691 proc.setPerformed(convert.makeDateTimeFromTS(cda.getChild(p, "effectiveTime"))); 692 693 for (Element e : cda.getChildren(p, "performer")) { 694 ProcedurePerformerComponent part = proc.addPerformer(); 695 Practitioner pp = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson"); 696 Reference ref = new Reference().setReference("Practitioner/" + pp.getId()).setDisplay(pp.getUserString("display")); 697 part.setActor(ref); 698 } 699 saveResource(proc); 700 } 701 saveResource(list); 702 } 703 704 private CodeableConcept inspectCode(CodeableConcept cc, Coding def) { 705 if (cc != null) { 706 for (Coding c : cc.getCoding()) { 707 if ("http://snomed.info/sct".equals(c.getSystem())) { 708 if ("ASSERTION".equals(c.getCode())) 709 c.setSystem("http://hl7.org/fhir/v3/ActCode"); 710 } 711 if ("http://hl7.org/fhir/v3/ActCode".equals(c.getSystem()) && "ASSERTION".equals(c.getCode())) { 712 if (def == null) 713 throw new Error("need a default code"); 714 c.setSystem(def.getSystem()); 715 c.setVersion(def.getVersion()); 716 c.setCode(def.getCode()); 717 c.setDisplay(def.getDisplay()); 718 } 719 } 720 } 721 return cc; 722 } 723 724 private ProcedureStatus determineProcedureStatus(Element child) { 725 if ("completed".equals(child.getAttribute("code"))) 726 return ProcedureStatus.COMPLETED; 727 throw new Error("not done yet: " + child.getAttribute("code")); 728 } 729 730 private void processReasonForEncounter(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception { 731 scanSection("Reason", sect); 732 context.getEncounter().addReason().setText(cda.getChild(sect, "text").getTextContent()); 733 } 734 735 private void processProblemsSection(CDAUtilities cda, Convert convert, Element sect, Context context) throws Exception { 736 scanSection("Problems", sect); 737 ListResource list = new ListResource(); 738 list.setId(context.getBaseId() + "-list-problems"); 739 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafproblemlist"); 740 list.setSubject(context.getSubjectRef()); 741 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sect, "code")), null)); 742 list.setTitle(cda.getChild(sect, "title").getTextContent()); 743 list.setStatus(ListStatus.CURRENT); 744 list.setMode(ListMode.SNAPSHOT); 745 list.setDateElement(context.getNow()); 746 list.setSource(context.getAuthorRef()); 747 buildNarrative(list, cda.getChild(sect, "text")); 748 749 int i = 0; 750 for (Element c : cda.getChildren(sect, "entry")) { 751 Element pca = cda.getChild(c, "act"); // problem concern act 752 Condition cond = new Condition(); 753 cond.setId(context.getBaseId() + "-problem-" + i); 754 cond.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/condition-daf-dafcondition"); 755 i++; 756 cond.setSubject(context.getSubjectRef()); 757 cond.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 758 cond.setVerificationStatus(getVerificationStatusFromAct(cda.getChild(pca, "statusCode"))); 759 760 cond.setAssertedDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(pca, "effectiveTime"), "low"))); 761 762 boolean found = false; 763 for (Element e : cda.getChildren(pca, "id")) { 764 Identifier id = convert.makeIdentifierFromII(e); 765 cond.getIdentifier().add(id); 766 } 767 if (!found) { 768 list.addEntry().setItem(new Reference().setReference("Condition/" + cond.getId())); 769 for (Element e : cda.getChildren(pca, "performer")) { 770 if (cond.hasAsserter()) 771 throw new Error("additional asserter discovered"); 772 Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson"); 773 Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")); 774 cond.setAsserter(ref); 775 } 776 Element po = cda.getChild(cda.getChild(pca, "entryRelationship"), "observation"); // problem observation 777 cond.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(po, "value")), null)); 778 recordConditionCode(cond.getCode()); 779 cond.setOnset(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(po, "effectiveTime"), "low"))); 780 Element pso = cda.getChild(cda.getChild(po, "entryRelationship"), "observation"); // problem status observation 781 String status = cda.getChild(pso, "value").getAttribute("code"); 782 if (status.equals("55561003")) 783 cond.setAbatement(new BooleanType("false")); 784 else 785 throw new Error("unknown status code " + status); 786 saveResource(cond); 787 } 788 } 789 saveResource(list); 790 } 791 792 private ConditionVerificationStatus getVerificationStatusFromAct(Element child) { 793 String s = child.getAttribute("code"); 794 if (!"active".equals(s)) 795 System.out.println(s); 796 return ConditionVerificationStatus.CONFIRMED; 797 } 798 799 private void processAllergiesSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 800 scanSection("Allergies", section); 801 ListResource list = new ListResource(); 802 list.setId(context.getBaseId() + "-list-allergies"); 803 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafallergylist"); 804 list.setSubject(context.getSubjectRef()); 805 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 806 list.setTitle(cda.getChild(section, "title").getTextContent()); 807 list.setStatus(ListStatus.CURRENT); 808 list.setDateElement(context.getNow()); 809 list.setSource(context.getAuthorRef()); 810 list.setMode(ListMode.SNAPSHOT); 811 buildNarrative(list, cda.getChild(section, "text")); 812 813 int i = 0; 814 for (Element c : cda.getChildren(section, "entry")) { 815 Element apa = cda.getChild(c, "act"); // allergy problem act 816 AllergyIntolerance ai = new AllergyIntolerance(); 817 ai.setId(context.getBaseId() + "-allergy-" + i); 818 ai.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/allergyintolerance-daf-dafallergyintolerance"); 819 i++; 820 ai.setPatient(context.getSubjectRef()); 821 822 ai.setAssertedDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(apa, "effectiveTime"), "low"))); 823 boolean found = false; 824 for (Element e : cda.getChildren(apa, "id")) { 825 Identifier id = convert.makeIdentifierFromII(e); 826 ai.getIdentifier().add(id); 827 } 828 if (!found) { 829 list.addEntry().setItem(new Reference().setReference("AllergyIntolerance/" + ai.getId())); 830 831 Element ao = cda.getChild(cda.getChild(apa, "entryRelationship"), "observation"); // allergy observation 832 if (!cda.getChild(ao, "value").getAttribute("code").equals("419511003")) 833 throw new Error("unexpected code"); 834 // nothing.... 835 836 // no allergy status observation 837 List<Element> reactions = cda.getChildren(ao, "entryRelationship"); 838 Element pe = cda.getChild(cda.getChild(cda.getChild(ao, "participant"), "participantRole"), "playingEntity"); 839 Element pec = cda.getChild(pe, "code"); 840 if (pec == null || !Utilities.noString(pec.getAttribute("nullFlavor"))) { 841 String n = cda.getChild(pe, "name").getTextContent(); 842 // if (n.contains("No Known Drug Allergies") && reactions.isEmpty()) 843 // ai.setSubstance(new CodeableConcept().setText(n)); // todo: what do with this? 844 // else 845 ai.setCode(new CodeableConcept().setText(n)); 846 } else 847 ai.setCode(inspectCode(convert.makeCodeableConceptFromCD(pec), null)); 848 recordAllergyCode(ai.getCode()); 849 if (!reactions.isEmpty()) { 850 AllergyIntoleranceReactionComponent aie = ai.addReaction(); 851 for (Element er : reactions) { 852 Element ro = cda.getChild(er, "observation"); 853 aie.addManifestation(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(ro, "value")), null)); 854 } 855 } 856 857 saveResource(ai); 858 } 859 } 860 saveResource(list); 861 } 862 863 private void processVitalSignsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 864 scanSection("Vital Signs", section); 865 ListResource list = new ListResource(); 866 list.setId(context.getBaseId() + "-list-vitalsigns"); 867 //. list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafproblemlist"); no list 868 list.setSubject(context.getSubjectRef()); 869 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 870 list.setTitle(cda.getChild(section, "title").getTextContent()); 871 list.setStatus(ListStatus.CURRENT); 872 list.setMode(ListMode.SNAPSHOT); 873 list.setDateElement(context.getNow()); 874 list.setSource(context.getAuthorRef()); 875 buildNarrative(list, cda.getChild(section, "text")); 876 877 int i = 0; 878 for (Element c : cda.getChildren(section, "entry")) { 879 Element org = cda.getChild(c, "organizer"); // problem concern act 880 for (Element oc : cda.getChildren(org, "component")) { 881 Element o = cda.getChild(oc, "observation"); // problem concern act 882 Observation obs = new Observation(); 883 obs.setId(context.getBaseId() + "-vitals-" + i); 884 obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-vitalsigns-dafvitalsigns"); 885 i++; 886 obs.setSubject(context.getSubjectRef()); 887 obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 888 obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), null)); 889 890 boolean found = false; 891 for (Element e : cda.getChildren(o, "id")) { 892 Identifier id = convert.makeIdentifierFromII(e); 893 obs.getIdentifier().add(id); 894 } 895 896 if (!found) { 897 list.addEntry().setItem(new Reference().setReference("Observation/" + obs.getId())); 898 obs.setStatus(ObservationStatus.FINAL); 899 obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime"))); 900 String v = cda.getChild(o, "value").getAttribute("value"); 901 if (!Utilities.isDecimal(v, true)) { 902 obs.setDataAbsentReason(inspectCode(new CodeableConcept().setText(v), null)); 903 } else 904 obs.setValue(convert.makeQuantityFromPQ(cda.getChild(o, "value"))); 905 saveResource(obs, "-vs"); 906 } 907 } 908 } 909 saveResource(list, "-vs"); 910 } 911 912 private void processResultsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 913 scanSection("Results", section); 914 915 ListResource list = new ListResource(); 916 list.setId(context.getBaseId() + "-list-results"); 917 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafresultlist"); 918 list.setSubject(context.getSubjectRef()); 919 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 920 list.setTitle(cda.getChild(section, "title").getTextContent()); 921 list.setStatus(ListStatus.CURRENT); 922 list.setMode(ListMode.SNAPSHOT); 923 list.setDateElement(context.getNow()); 924 list.setSource(context.getAuthorRef()); 925 buildNarrative(list, cda.getChild(section, "text")); 926 927 context.setObsId(0); 928 for (Element c : cda.getChildren(section, "entry")) { 929 Element org = cda.getChild(c, "organizer"); 930 if (org != null) { 931 Observation panel = new Observation(); 932 panel.setId(context.getBaseId() + "-results-" + context.getObsId()); 933 panel.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobspanel"); 934 context.setObsId(context.getObsId() + 1); 935 panel.setSubject(context.getSubjectRef()); 936 panel.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 937 panel.setStatus(ObservationStatus.FINAL); 938 boolean found = false; 939 for (Element e : cda.getChildren(org, "id")) { 940 Identifier id = convert.makeIdentifierFromII(e); 941 panel.getIdentifier().add(id); 942 } 943 if (!found) { 944 list.addEntry().setItem(new Reference().setReference("Observation/" + panel.getId())); 945 946 panel.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(org, "code")), null)); 947 for (Element comp : cda.getChildren(org, "component")) { 948 Observation obs = processObservation(cda, convert, context, cda.getChild(comp, "observation")); 949 panel.addRelated().setType(ObservationRelationshipType.HASMEMBER).setTarget(new Reference().setReference("Observation/" + obs.getId())); 950 if (!panel.hasEffective()) 951 panel.setEffective(obs.getEffective()); 952 else { 953 if (!Base.compareDeep(panel.getEffective(), obs.getEffective(), false)) { 954 Period p = panel.getEffective() instanceof Period ? panel.getEffectivePeriod() : new Period().setStartElement(panel.getEffectiveDateTimeType()).setEndElement(panel.getEffectiveDateTimeType()); 955 if (p.getStartElement().after(obs.getEffectiveDateTimeType())) 956 p.setStartElement(obs.getEffectiveDateTimeType()); 957 if (p.getEndElement().before(obs.getEffectiveDateTimeType())) 958 p.setEndElement(obs.getEffectiveDateTimeType()); 959 panel.setEffective(p); 960 } 961 } 962 963 } 964 saveResource(panel, "-res"); 965 } 966 } 967 Element o = cda.getChild(c, "observation"); 968 if (o != null) { 969 Observation obs = processObservation(cda, convert, context, o); 970 list.addEntry().setItem(new Reference().setReference("Observation/" + obs.getId())); 971 } 972 } 973 saveResource(list, "-res"); 974 } 975 976 private Observation processObservation(CDAUtilities cda, Convert convert, Context context, Element o) throws Exception { 977 Observation obs = new Observation(); 978 obs.setId(context.getBaseId() + "-results-" + context.getObsId()); 979 context.setObsId(context.getObsId() + 1); 980 obs.setSubject(context.getSubjectRef()); 981 obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 982 obs.setStatus(ObservationStatus.FINAL); 983 obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime"))); 984 obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), null)); 985 obs.setInterpretation(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "interpretationCode")), null)); 986 Element rr = cda.getChild(o, "referenceRange"); 987 if (rr != null) 988 obs.addReferenceRange().setText(cda.getChild(cda.getChild(rr, "observationRange"), "text").getTextContent()); 989 990 Element v = cda.getChild(o, "value"); 991 String type = v.getAttribute("xsi:type"); 992 if ("ST".equals(type)) { 993 obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobsother"); 994 obs.setValue(new StringType(v.getTextContent())); 995 } else if ("CD".equals(type)) { 996 obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobscode"); 997 obs.setValue(inspectCode(convert.makeCodeableConceptFromCD(v), null)); 998 } else if ("PQ".equals(type)) { 999 obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-results-dafresultobsquantity"); 1000 String va = cda.getChild(o, "value").getAttribute("value"); 1001 if (!Utilities.isDecimal(va, true)) { 1002 obs.setDataAbsentReason(inspectCode(new CodeableConcept().setText(va), null)); 1003 } else 1004 obs.setValue(convert.makeQuantityFromPQ(cda.getChild(o, "value"), null)); 1005 } else 1006 throw new Exception("Unknown type '" + type + "'"); 1007 1008 for (Element e : cda.getChildren(o, "id")) { 1009 Identifier id = convert.makeIdentifierFromII(e); 1010 obs.getIdentifier().add(id); 1011 } 1012 saveResource(obs, "-gen"); 1013 return obs; 1014 } 1015 1016 private void processSocialHistorySection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 1017 scanSection("Social History", section); 1018 int i = 0; 1019 for (Element c : cda.getChildren(section, "entry")) { 1020 Element o = cda.getChild(c, "observation"); 1021 Observation obs = new Observation(); 1022 obs.setId(context.getBaseId() + "-smoking-" + (i == 0 ? "" : Integer.toString(i))); 1023 obs.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/observation-daf-smokingstatus-dafsmokingstatus"); 1024 i++; 1025 obs.setSubject(context.getSubjectRef()); 1026 obs.setContext(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 1027 obs.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "code")), new Coding().setSystem("http://loinc.org").setCode("72166-2"))); 1028 1029 boolean found = false; 1030 for (Element e : cda.getChildren(o, "id")) { 1031 Identifier id = convert.makeIdentifierFromII(e); 1032 obs.getIdentifier().add(convert.makeIdentifierFromII(e)); 1033 } 1034 if (!found) { 1035 obs.setStatus(ObservationStatus.FINAL); 1036 obs.setEffective(convert.makeDateTimeFromTS(cda.getChild(o, "effectiveTime"))); 1037 obs.setValue(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(o, "value")), null)); 1038 saveResource(obs, "-sh"); 1039 } 1040 } 1041 } 1042 1043 private void processMedicationsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 1044 scanSection("Medications", section); 1045 ListResource list = new ListResource(); 1046 list.setId(context.getBaseId() + "-list-medications"); 1047 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafmedicationlist"); 1048 list.setSubject(context.getSubjectRef()); 1049 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 1050 list.setTitle(cda.getChild(section, "title").getTextContent()); 1051 list.setStatus(ListStatus.CURRENT); 1052 list.setMode(ListMode.SNAPSHOT); 1053 list.setDateElement(context.getNow()); 1054 list.setSource(context.getAuthorRef()); 1055 buildNarrative(list, cda.getChild(section, "text")); 1056 1057 int i = 0; 1058 for (Element c : cda.getChildren(section, "entry")) { 1059 Element sa = cda.getChild(c, "substanceAdministration"); // allergy problem act 1060 MedicationStatement ms = new MedicationStatement(); 1061 ms.setId(context.getBaseId() + "-medication-" + i); 1062 ms.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/medicationstatement-daf-dafmedicationstatement"); 1063 i++; 1064 ms.setSubject(context.getSubjectRef()); 1065 1066 boolean found = false; 1067 for (Element e : cda.getChildren(sa, "id")) { 1068 Identifier id = convert.makeIdentifierFromII(e); 1069 ms.getIdentifier().add(id); 1070 } 1071 if (!found) { 1072 ms.setStatus(MedicationStatementStatus.COMPLETED); 1073 list.addEntry().setItem(new Reference().setReference("MedicationStatement/" + ms.getId())); 1074 1075 Element mm = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturedMaterial"); // allergy observation 1076 ms.setMedication(new Reference().setReference("#med")); 1077 Medication med = new Medication(); 1078 med.setId("med"); 1079 med.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(mm, "code")), null)); 1080 ms.getContained().add(med); 1081 Dosage dosage = ms.addDosage(); 1082 Element qty = cda.getChild(sa, "doseQuantity"); // allergy observation 1083 try { 1084 if (cda.getChild(qty, "low") != null) { 1085 // todo: this is not correct? 1086 dosage.getExtension().add(new Extension().setUrl("http://healthintersections.com.au/fhir/extensions/medication-statement-range").setValue(convert.makeRangeFromIVLPQ(qty))); 1087 } else { 1088 dosage.setDose(convert.makeQuantityFromPQ(qty)); 1089 } 1090 } catch (Exception e) { 1091 System.out.println(" invalid dose quantity '" + qty.getAttribute("value") + " " + qty.getAttribute("unit") + "' (" + e.getClass().getName() + ") in " + context.getBaseId()); 1092 } 1093 dosage.setRoute(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sa, "routeCode")), null)); 1094 Type t = convert.makeSomethingFromGTS(cda.getChildren(sa, "effectiveTime")); 1095 if (t instanceof Timing) { 1096 dosage.setTiming((Timing) t); 1097 if (dosage.getTiming().hasRepeat() && dosage.getTiming().getRepeat().hasBounds()) 1098 ms.setEffective(dosage.getTiming().getRepeat().getBounds()); 1099 } else if (t instanceof Period) 1100 ms.setEffective(t); 1101 else 1102 throw new Exception("Undecided how to handle " + t.getClass().getName()); 1103 1104 for (Element e : cda.getChildren(sa, "author")) { 1105 if (ms.hasInformationSource()) 1106 throw new Error("additional author discovered"); 1107 Practitioner p = processPerformer(cda, convert, context, e, "assignedAuthor", "assignedPerson"); 1108 Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")); 1109 ms.setInformationSource(ref); 1110 ms.setDateAssertedElement(convert.makeDateTimeFromTS(cda.getChild(e, "time"))); 1111 } 1112 saveResource(ms); 1113 } 1114 } 1115 saveResource(list); 1116 } 1117 1118 private void processEncountersSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 1119 scanSection("Encounters", section); 1120 ListResource list = new ListResource(); 1121 list.setId(context.getBaseId() + "-list-encounters"); 1122 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafencounterlist"); 1123 list.setSubject(context.getSubjectRef()); 1124 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 1125 list.setTitle(cda.getChild(section, "title").getTextContent()); 1126 list.setStatus(ListStatus.CURRENT); 1127 list.setMode(ListMode.SNAPSHOT); 1128 list.setDateElement(context.getNow()); 1129 list.setSource(context.getAuthorRef()); 1130 buildNarrative(list, cda.getChild(section, "text")); 1131 1132 int i = 0; 1133 for (Element c : cda.getChildren(section, "entry")) { 1134 Element ee = cda.getChild(c, "encounter"); // allergy problem act 1135 Encounter enc = new Encounter(); 1136 enc.setId(context.getBaseId() + "-encounter-" + i); 1137 enc.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/encounter-daf-dafencounter"); 1138 i++; 1139 enc.setSubject(context.getSubjectRef()); 1140 list.addEntry().setItem(new Reference().setReference("Encounter/" + enc.getId())); 1141 1142 for (Element e : cda.getChildren(ee, "id")) 1143 enc.getIdentifier().add(convert.makeIdentifierFromII(e)); 1144 checkGenerateIdentifier(enc.getIdentifier(), enc); 1145 1146 1147 enc.setPeriod(convert.makePeriodFromIVL(cda.getChild(ee, "effectiveTime"))); 1148 if (enc.getPeriod().hasEnd()) 1149 enc.setStatus(EncounterStatus.FINISHED); 1150 else 1151 enc.setStatus(EncounterStatus.INPROGRESS); 1152 1153 if (cda.getChild(ee, "text") != null) 1154 enc.setClass_(convertTextToCoding(cda.getChild(ee, "text").getTextContent().trim())); 1155 else 1156 enc.setClass_(null); // todo: fix this 1157 1158 CodeableConcept type = inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(ee, "code")), null); 1159 enc.addType(type); 1160 1161 for (Element e : cda.getChildren(ee, "performer")) { 1162 Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson"); 1163 Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")); 1164 enc.addParticipant().setIndividual(ref).setPeriod(convert.makePeriodFromIVL(cda.getChild(e, "time"))); 1165 } 1166 enc.addLocation().setLocation(new Reference().setReference("#loc")); 1167 Location loc = new Location(); 1168 loc.setId("loc"); 1169 Element pr = cda.getChild(cda.getChild(ee, "participant"), "participantRole"); 1170 loc.setName(cda.getChild(cda.getChild(pr, "playingEntity"), "name").getTextContent()); 1171 loc.setType(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(pr, "code")), null)); 1172 enc.getContained().add(loc); 1173 saveResource(enc); 1174 } 1175 saveResource(list); 1176 } 1177 1178 private Coding convertTextToCoding(String v) { 1179 v = v.toLowerCase(); 1180 if (v.equals("inpatient")) 1181 return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("IMP"); 1182 if (v.equals("emergency department") || v.equals("emergency department admit decision")) 1183 return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("EMER"); 1184 if (v.equals("x-ray exam")) 1185 return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB"); 1186 if (v.equals("outpatient")) 1187 return new Coding().setSystem("http://hl7.org/fhir/v3/ActCode").setCode("AMB"); 1188 throw new Error("unknown encounter type " + v); 1189 } 1190 1191 private void processImmunizationsSection(CDAUtilities cda, Convert convert, Element section, Context context) throws Exception { 1192 scanSection("Immunizations", section); 1193 ListResource list = new ListResource(); 1194 list.setId(context.getBaseId() + "-list-immunizations"); 1195 list.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/list-daf-dafimmunizationlist"); 1196 list.setSubject(context.getSubjectRef()); 1197 list.setCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(section, "code")), null)); 1198 list.setTitle(cda.getChild(section, "title").getTextContent()); 1199 list.setStatus(ListStatus.CURRENT); 1200 list.setMode(ListMode.SNAPSHOT); 1201 list.setDateElement(context.getNow()); 1202 list.setSource(context.getAuthorRef()); 1203 buildNarrative(list, cda.getChild(section, "text")); 1204 1205 int i = 0; 1206 for (Element c : cda.getChildren(section, "entry")) { 1207 Element sa = cda.getChild(c, "substanceAdministration"); // allergy problem act 1208 Immunization imm = new Immunization(); 1209 imm.setId(context.getBaseId() + "-immunization-" + i); 1210 imm.setUserData("profile", "http://hl7.org/fhir/StructureDefinition/immunization-daf-dafimmunization"); 1211 i++; 1212 imm.setPatient(context.getSubjectRef()); 1213 imm.setEncounter(new Reference().setReference("Encounter/" + context.getEncounter().getId())); 1214 imm.setNotGiven("true".equals(sa.getAttribute("negationInd"))); 1215 imm.setStatus(convertImmunizationStatus(cda.getChild(sa, "statusCode"))); 1216 boolean found = false; 1217 for (Element e : cda.getChildren(sa, "id")) { 1218 Identifier id = convert.makeIdentifierFromII(e); 1219 imm.getIdentifier().add(id); 1220 } 1221 if (!found) { 1222 list.addEntry().setItem(new Reference().setReference("Immunization/" + imm.getId())); 1223 1224 imm.setDateElement(convert.makeDateTimeFromTS(cda.getChild(cda.getChild(sa, "effectiveTime"), "low"))); 1225 if (imm.getNotGiven()) { 1226 Element reason = cda.getChild(cda.getChildByAttribute(sa, "entryRelationship", "typeCode", "RSON"), "observation"); 1227 imm.setExplanation(new ImmunizationExplanationComponent()); 1228 imm.getExplanation().addReasonNotGiven(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(reason, "code")), null)); 1229 } 1230 Element mm = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturedMaterial"); 1231 imm.setVaccineCode(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(mm, "code")), null)); 1232 imm.setRoute(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(sa, "routeCode")), null)); 1233 if (cda.getChild(mm, "lotNumberText") != null) 1234 imm.setLotNumber(cda.getChild(mm, "lotNumberText").getTextContent()); 1235 Element mr = cda.getChild(cda.getChild(cda.getChild(sa, "consumable"), "manufacturedProduct"), "manufacturerOrganization"); 1236 if (mr != null) 1237 imm.setManufacturer(new Reference().setDisplay(cda.getChild(mr, "name").getTextContent())); 1238 1239 // the problem with this is that you can't have just a dose sequence number 1240 // Element subject = cda.getChild(cda.getChildByAttribute(sa, "entryRelationship", "typeCode", "SUBJ"), "observation"); 1241 // if (subject != null) 1242 // imm.addVaccinationProtocol().setDoseSequence(Integer.parseInt(cda.getChild(subject, "value").getAttribute("value"))); 1243 1244 boolean hasprf = false; 1245 for (Element e : cda.getChildren(sa, "performer")) { 1246 if (imm.hasPractitioner()) 1247 throw new Error("additional performer discovered"); 1248 Practitioner p = processPerformer(cda, convert, context, e, "assignedEntity", "assignedPerson"); 1249 Reference ref = new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display")); 1250 imm.addPractitioner().setActor(ref).setRole(new org.hl7.fhir.dstu3.model.CodeableConcept().addCoding(new Coding().setSystem("http://hl7.org/fhir/v2/0443").setCode("AP"))); 1251 hasprf = true; 1252 } 1253 imm.setPrimarySource(hasprf); 1254 saveResource(imm); 1255 } 1256 } 1257 saveResource(list); 1258 } 1259 1260 private ImmunizationStatus convertImmunizationStatus(Element child) { 1261 String s = child.getAttribute("code"); 1262 if (s.equals("completed")) 1263 return ImmunizationStatus.COMPLETED; 1264 throw new Error("Unexpected status " + s); 1265 } 1266 1267 // /informationRecipient: 2979 1268 // /informationRecipient/intendedRecipient: 2979 1269 // /informationRecipient/intendedRecipient/addr: 2979 1270 // /informationRecipient/intendedRecipient/informationRecipient: 2979 1271 // /informationRecipient/intendedRecipient/informationRecipient/name: 2979 1272 // /informationRecipient/intendedRecipient/receivedOrganization: 2979 1273 // /informationRecipient/intendedRecipient/receivedOrganization/addr: 2979 1274 // /informationRecipient/intendedRecipient/receivedOrganization/id: 2979 1275 // /informationRecipient/intendedRecipient/receivedOrganization/name: 2979 1276 // /informationRecipient/intendedRecipient/receivedOrganization/telecom: 2979 1277 1278 private void makeBinary(String sourceFolder, String filename, Context context) throws Exception { 1279 Binary binary = new Binary(); 1280 binary.setId(context.getBaseId() + "-binary"); 1281 binary.setContentType("application/hl7-v3+xml"); 1282 binary.setContent(IOUtils.toByteArray(new FileInputStream(Utilities.path(sourceFolder, filename)))); 1283 saveResource(binary); 1284 } 1285 1286 private void makeDocumentReference(CDAUtilities cda, Convert convert, Element doc, Context context) throws Exception { 1287 scanSection("document", doc); 1288 DocumentReference ref = new DocumentReference(); 1289 ref.setId(context.getBaseId() + "-document"); 1290 ref.setMasterIdentifier(convert.makeIdentifierFromII(cda.getChild(doc, "id"))); 1291 ref.setSubject(context.getSubjectRef()); 1292 ref.setType(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(doc, "code")), null)); 1293 ref.addAuthor(context.getAuthorRef()); 1294 ref.setCreatedElement(convert.makeDateTimeFromTS(cda.getChild(doc, "effectiveTime"))); 1295 ref.setIndexedElement(InstantType.now()); 1296 ref.setStatus(DocumentReferenceStatus.CURRENT); 1297 ref.addSecurityLabel(inspectCode(convert.makeCodeableConceptFromCD(cda.getChild(doc, "confidentialityCode")), null)); 1298 DocumentReferenceContentComponent cnt = ref.addContent(); 1299 cnt.getAttachment().setContentType("application/hl7-v3+xml").setUrl("Binary/" + context.getBaseId()).setLanguage(convertLanguage(cda.getChild(doc, "language"))); 1300 // for (Element ti : cda.getChildren(doc, "templateId")) 1301 // cnt.addFormat().setSystem("urn:oid:1.3.6.1.4.1.19376.1.2.3").setCode(value)("urn:oid:"+ti.getAttribute("root")); 1302 ref.setContext(new DocumentReferenceContextComponent()); 1303 ref.getContext().setPeriod(convert.makePeriodFromIVL(cda.getChild(cda.getChild(doc, "serviceEvent"), "effectiveTime"))); 1304 for (CodeableConcept cc : context.getEncounter().getType()) 1305 ref.getContext().addEvent(cc); 1306 ref.setDescription(cda.getChild(doc, "title").getTextContent()); 1307 ref.setCustodian(new Reference().setReference("Organization/" + processOrganization(cda.getDescendent(doc, "custodian/assignedCustodian/representedCustodianOrganization"), cda, convert, context).getId())); 1308 Practitioner p = processPerformer(cda, convert, context, cda.getChild(doc, "legalAuthenticator"), "assignedEntity", "assignedPerson"); 1309 ref.setAuthenticator(new Reference().setReference("Practitioner/" + p.getId()).setDisplay(p.getUserString("display"))); 1310 saveResource(ref); 1311 } 1312 1313 private String convertLanguage(Element child) { 1314 if (child == null) 1315 return null; 1316 return child.getAttribute("code"); 1317 } 1318 1319 private CodeableConcept makeClassCode(CodeableConcept type, DocumentReference ref) throws Exception { 1320 CodeableConcept res = new CodeableConcept(); 1321 String cs = type.getCoding().get(0).getCode(); 1322 if (cs.equals("18842-5") || cs.equals("34133-9")) 1323 return type; 1324 else if (cs.equals("34111-5")) { 1325 ref.getFormatCommentsPre().add("The underlying CDA document has the code '34111-5: Evaluation and Management Note' which is incorrect (wrong display/code combination). The type has been preserved even though it's wrong"); 1326 res.addCoding().setSystem("http://loinc.org").setCode("34109-9").setDisplay("Evaluation and management note"); 1327 } 1328 // else if (cs.equals("34111-5") || cs.equals("5666")) 1329 // res.addCoding().setSystem("http://loinc.org").setCode("LP173418-7").setDisplay("Note"); 1330 else 1331 throw new Exception("Uncategorised document type code: " + cs + ": " + type.getCoding().get(0).getDisplay()); 1332 return res; 1333 1334 } 1335 1336 private void recordProcedureCode(CodeableConcept code) { 1337 for (Coding c : code.getCoding()) { 1338 count(c, procCodes); 1339 } 1340 } 1341 1342 private void count(Coding c, Map<String, Integer> map) { 1343 String s = c.getSystem() + "::" + c.getCode(); 1344 if (map.containsKey(s)) 1345 map.put(s, map.get(s) + 1); 1346 else 1347 map.put(s, 1); 1348 } 1349 1350 private void recordConditionCode(CodeableConcept code) { 1351 for (Coding c : code.getCoding()) { 1352 count(c, condCodes); 1353 } 1354 } 1355 1356 private void recordAllergyCode(CodeableConcept code) { 1357 for (Coding c : code.getCoding()) { 1358 count(c, allergyCodes); 1359 } 1360 } 1361 1362 private void dumpCodes() { 1363 dump("Procedure Codes", procCodes); 1364 dump("Condition Codes", condCodes); 1365 dump("Allergy Codes", allergyCodes); 1366 } 1367 1368 private void dump(String string, Map<String, Integer> map) { 1369 System.out.println(string); 1370 System.out.println(); 1371 for (String s : map.keySet()) { 1372 System.out.println(s + ": " + map.get(s)); 1373 } 1374 System.out.println(); 1375 System.out.println(); 1376 } 1377 1378}