001package org.hl7.fhir.dstu2.terminologies;
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
032import java.io.FileInputStream;
033import java.io.FileNotFoundException;
034import java.io.FileOutputStream;
035import java.io.IOException;
036
037import javax.xml.parsers.DocumentBuilder;
038import javax.xml.parsers.DocumentBuilderFactory;
039import javax.xml.parsers.ParserConfigurationException;
040
041import org.hl7.fhir.dstu2.formats.XmlParser;
042import org.hl7.fhir.dstu2.model.Bundle;
043import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent;
044import org.hl7.fhir.dstu2.model.Bundle.BundleType;
045import org.hl7.fhir.dstu2.model.CodeableConcept;
046import org.hl7.fhir.dstu2.model.Coding;
047import org.hl7.fhir.dstu2.model.DataElement;
048import org.hl7.fhir.dstu2.model.DateTimeType;
049import org.hl7.fhir.dstu2.model.ElementDefinition;
050import org.hl7.fhir.dstu2.model.Enumerations.ConformanceResourceStatus;
051import org.hl7.fhir.dstu2.model.Identifier;
052import org.hl7.fhir.dstu2.model.InstantType;
053import org.hl7.fhir.dstu2.model.Meta;
054import org.hl7.fhir.dstu2.utils.ToolingExtensions;
055import org.hl7.fhir.exceptions.FHIRFormatError;
056import org.hl7.fhir.utilities.Utilities;
057import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
058import org.hl7.fhir.utilities.xml.XMLUtil;
059import org.w3c.dom.Document;
060import org.w3c.dom.Element;
061import org.xml.sax.SAXException;
062import org.xmlpull.v1.XmlPullParserException;
063
064/**
065 * This class converts the LOINC XML representation that the FHIR build tool
066 * uses internally to a set of DataElements in an atom feed
067 * 
068 * @author Grahame
069 *
070 */
071@Deprecated
072public class LoincToDEConvertor {
073
074  public static void main(String[] args)
075      throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException {
076    if (args.length == 0) {
077      System.out.println("FHIR LOINC to CDE convertor. ");
078      System.out.println("");
079      System.out.println("This tool converts from LOINC to A set of DataElement definitions.");
080      System.out.println("");
081      System.out.println("Usage: [jar(path?)] [dest] (-defn [definitions]) where: ");
082      System.out.println("* [dest] is a file name of the bundle to produce");
083      System.out.println(
084          "* [definitions] is the file name of a file produced by exporting the main LOINC table from the mdb to XML");
085      System.out.println("");
086    } else {
087      LoincToDEConvertor exe = new LoincToDEConvertor();
088      exe.setDest(args[0]);
089      for (int i = 1; i < args.length; i++) {
090        if (args[i].equals("-defn"))
091          exe.setDefinitions(args[i + 1]);
092      }
093      exe.process();
094    }
095
096  }
097
098  private String dest;
099  private String definitions;
100
101  public String getDest() {
102    return dest;
103  }
104
105  public void setDest(String dest) {
106    this.dest = dest;
107  }
108
109  public String getDefinitions() {
110    return definitions;
111  }
112
113  public void setDefinitions(String definitions) {
114    this.definitions = definitions;
115  }
116
117  private Document xml;
118  private Bundle bundle;
119  private DateTimeType now;
120
121  public Bundle process(String sourceFile)
122      throws FileNotFoundException, SAXException, IOException, ParserConfigurationException {
123    this.definitions = sourceFile;
124    log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions);
125    loadLoinc();
126    log("LOINC loaded");
127
128    now = DateTimeType.now();
129
130    bundle = new Bundle();
131    bundle.setType(BundleType.COLLECTION);
132    bundle.setId("http://hl7.org/fhir/commondataelement/loinc");
133    bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now()));
134
135    processLoincCodes();
136    return bundle;
137  }
138
139  public void process()
140      throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException {
141    log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions);
142    loadLoinc();
143    log("LOINC loaded");
144
145    now = DateTimeType.now();
146
147    bundle = new Bundle();
148    bundle.setId("http://hl7.org/fhir/commondataelement/loinc");
149    bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now()));
150
151    processLoincCodes();
152    if (dest != null) {
153      log("Saving...");
154      saveBundle();
155    }
156    log("Done");
157
158  }
159
160  private void log(String string) {
161    System.out.println(string);
162
163  }
164
165  private void loadLoinc() throws FileNotFoundException, SAXException, IOException, ParserConfigurationException {
166    DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory();
167    factory.setNamespaceAware(true);
168    DocumentBuilder builder = factory.newDocumentBuilder();
169
170    xml = builder.parse(ManagedFileAccess.inStream(definitions));
171  }
172
173  private void saveBundle() throws FHIRFormatError, IOException, XmlPullParserException {
174    XmlParser xml = new XmlParser();
175    FileOutputStream s = ManagedFileAccess.outStream(dest);
176    xml.compose(s, bundle, true);
177    s.close();
178  }
179
180  private String col(Element row, String name) {
181    Element e = XMLUtil.getNamedChild(row, name);
182    if (e == null)
183      return null;
184    String text = e.getTextContent();
185    return text;
186  }
187
188  private boolean hasCol(Element row, String name) {
189    return Utilities.noString(col(row, name));
190  }
191
192  private void processLoincCodes() {
193    Element row = XMLUtil.getFirstChild(xml.getDocumentElement());
194    int i = 0;
195    while (row != null) {
196      i++;
197      if (i % 1000 == 0)
198        System.out.print(".");
199      String code = col(row, "LOINC_NUM");
200      String comp = col(row, "COMPONENT");
201      DataElement de = new DataElement();
202      de.setId("loinc-" + code);
203      de.setMeta(new Meta().setLastUpdatedElement(InstantType.now()));
204      bundle.getEntry().add(new BundleEntryComponent().setResource(de));
205      Identifier id = new Identifier();
206      id.setSystem("http://hl7.org/fhir/commondataelement/loinc");
207      id.setValue(code);
208      de.addIdentifier(id);
209      de.setPublisher("Regenstrief + FHIR Project Team");
210      if (!col(row, "STATUS").equals("ACTIVE"))
211        de.setStatus(ConformanceResourceStatus.DRAFT); // till we get good at this
212      else
213        de.setStatus(ConformanceResourceStatus.RETIRED);
214      de.setDateElement(DateTimeType.now());
215      de.setName(comp);
216      ElementDefinition dee = de.addElement();
217
218      // PROPERTY ignore
219      // TIME_ASPCT
220      // SYSTEM
221      // SCALE_TYP
222      // METHOD_TYP
223      // dee.getCategory().add(new CodeableConcept().setText(col(row, "CLASS")));
224      // SOURCE
225      // DATE_LAST_CHANGED - should be in ?
226      // CHNG_TYPE
227      dee.setComments(col(row, "COMMENTS"));
228      if (hasCol(row, "CONSUMER_NAME"))
229        dee.addAlias(col(row, "CONSUMER_NAME"));
230      // MOLAR_MASS
231      // CLASSTYPE
232      // FORMULA
233      // SPECIES
234      // EXMPL_ANSWERS
235      // ACSSYM
236      // BASE_NAME - ? this is a relationship
237      // NAACCR_ID
238      // ---------- CODE_TABLE todo
239      // SURVEY_QUEST_TEXT
240      // SURVEY_QUEST_SRC
241      if (hasCol(row, "RELATEDNAMES2")) {
242        String n = col(row, "RELATEDNAMES2");
243        for (String s : n.split("\\;")) {
244          if (!Utilities.noString(s))
245            dee.addAlias(s);
246        }
247      }
248      dee.addAlias(col(row, "SHORTNAME"));
249      // ORDER_OBS
250      // CDISC Code
251      // HL7_FIELD_SUBFIELD_ID
252      // ------------------ EXTERNAL_COPYRIGHT_NOTICE todo
253      dee.setDefinition(col(row, "LONG_COMMON_NAME"));
254      // HL7_V2_DATATYPE
255      String cc = makeType(col(row, "HL7_V3_DATATYPE"), code);
256      if (cc != null)
257        dee.addType().setCode(cc);
258      // todo... CURATED_RANGE_AND_UNITS
259      // todo: DOCUMENT_SECTION
260      // STATUS_REASON
261      // STATUS_TEXT
262      // CHANGE_REASON_PUBLIC
263      // COMMON_TEST_RANK
264      // COMMON_ORDER_RANK
265      // COMMON_SI_TEST_RANK
266      // HL7_ATTACHMENT_STRUCTURE
267
268      // units:
269      // UNITSREQUIRED
270      // SUBMITTED_UNITS
271      ToolingExtensions.setAllowableUnits(dee, makeUnits(col(row, "EXAMPLE_UNITS"), col(row, "EXAMPLE_UCUM_UNITS")));
272      // EXAMPLE_SI_UCUM_UNITS
273
274      row = XMLUtil.getNextSibling(row);
275    }
276    System.out.println("done");
277  }
278
279  private String makeType(String type, String id) {
280    if (Utilities.noString(type))
281      return null;
282    if (type.equals("PQ"))
283      return "Quantity";
284    else if (type.equals("ED"))
285      return "Attachment";
286    else if (type.equals("TS"))
287      return "dateTime";
288    else if (type.equals("ST"))
289      return "string";
290    else if (type.equals("II"))
291      return "Identifier";
292    else if (type.equals("CWE"))
293      return "CodeableConcept";
294    else if (type.equals("CD") || type.equals("CO"))
295      return "CodeableConcept";
296    else if (type.equals("PN"))
297      return "HumanName";
298    else if (type.equals("EN"))
299      return "HumanName";
300    else if (type.equals("AD"))
301      return "Address";
302    else if (type.equals("BL"))
303      return "boolean";
304    else if (type.equals("GTS"))
305      return "Schedule";
306    else if (type.equals("INT"))
307      return "integer";
308    else if (type.equals("CS"))
309      return "code";
310    else if (type.equals("IVL_TS"))
311      return "Period";
312    else if (type.equals("MMAT") || type.equals("PRF") || type.equals("TX") || type.equals("DT") || type.equals("FT"))
313      return null;
314    else
315      throw new Error("unmapped type " + type + " for LOINC code " + id);
316  } // 18606-4: MMAT. 18665-0: PRF. 18671-8: TX. 55400-6: DT; 8251-1: FT
317
318  private CodeableConcept makeUnits(String text, String ucum) {
319    if (Utilities.noString(text) && Utilities.noString(ucum))
320      return null;
321    CodeableConcept cc = new CodeableConcept();
322    cc.setText(text);
323    cc.getCoding().add(new Coding().setCode(ucum).setSystem("http://unitsofmeasure.org"));
324    return cc;
325  }
326
327  public Bundle getBundle() {
328    return bundle;
329  }
330}