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