Attached below is are two projects we used to help one of our partners (an EMR vendor called Med Evolve) generate a CDA document to allow them to pass meaningful use certification. The project was highly successful! Med Evolve was able to implement our solution quickly: within two weeks, they had their software generating a working CDA document that passed the NIST certification.
The structure of these projects is deliberately simple. Within iNTERFACEWARE we had a tried several different methodologies for generating CDA documents before settling on this technique. The idea is to avoid deep abstraction! The code starts by creating an empty CDA XML tree. Then there are a series of sections designed to populate out different parts of the CDA document. The template makes use of a number of helper functions arranged in a convenient nested library of modules that work seamlessly with Translator’s deep auto-completion feature. We don’t attempt to support every nook and cranny of the CDA data model; instead, we use this very shallow abstraction to make it dead easy for anyone to apply (or extend) the helper functions as needed. In fact, because you are always operating off the raw XML tree, you can even manually add an XML attribute or tag directly, if required. As a result, all the special cases end up being very local and in the precise spot you need them. This makes your resulting CDA document transparent and easy to maintain.
Most of the functions have been written in a manner where it’s easy to see the inputs and outputs. Once again, this is very deliberate part of our design. We want to make it easier for you to know where to look, should you need to correct the data showing up in any given section.
There are other CDA frameworks which take an opposite approach to this. They tend to completely abstract away from the underlying XML, requiring you to populate a series of templates which are then mapped into the XML. The trouble with this approach is that it’s very brittle. If the framework doesn’t generate what you need, then you are out of luck. It’s very difficult to know where to fix the framework or implement a core change. This all makes for very hard-to-maintain code.
This example is hard-coded with dummy data. To make it into a real project, you will need to implement some standard Iguana techniques to pull and map incoming data from either messages or a database.
Here’s a snippet from the ‘main’ file of this project. This demonstrations how some of the ‘cda’ functions are used:
function GenerateCDA(PatientId) local Doc = cda.new() cda.setAttr(Doc, 'xmlns', 'voc', 'urn:hl7-org:v3/voc') -- Header header.document(Doc) header.patientInfo(Doc, PatientId) header.otherHeaderInfo(Doc) -- Body local Body = cda.createBody(Doc) -- History of Present Illness body.presentIllness(Body) -- Past Medical History local PMH = body.pastMedicalHistory.new(Body) body.pastMedicalHistory.asthma(PMH) body.pastMedicalHistory.hypertension(PMH) body.pastMedicalHistory.osteoarthritis(PMH) -- Medications -- This section better shows how one might generated -- the XML inside the <text> of the section. local M, MT = body.medications.new(Body) local ML = MT:append(xml.ELEMENT, 'list') body.medications.theodur(M, ML) body.medications.proventil(M, ML) body.medications.prednisone(M, ML) body.medications.hctz(M, ML) -- Allergies & Adverse Reactions local AAR = body.allergies.new(Body) body.allergies.penicillin(AAR) body.allergies.aspirin(AAR) body.allergies.codeine(AAR) body.allergies.nausea(AAR) -- Family History local FH = body.familyHistory.new(Body) body.familyHistory.mi(FH) body.familyHistory.cancerAndDiabetes(FH) -- Social History local SH = body.socialHistory.new(Body) body.socialHistory.populate(SH) -- Physical Exam local PE = body.physicalExam.new(Body) body.physicalExam.vitalSigns(PE) body.physicalExam.skin(PE) body.physicalExam.lungs(PE) body.physicalExam.cardiac(PE) -- Labs local L = body.labs.new(Body) body.labs.imaging(L) body.labs.peakFlow(L) -- In-office Procedure local IOP, IOPT = body.inofficeProcedure.new(Body) local IOPL = IOPT:append(xml.ELEMENT, 'list') body.inofficeProcedure.sutureRemoval(IOP, IOPL) -- Problems local P = body.problems.new(Body) body.problems.asthma(P) body.problems.hyperTension(P) body.problems.contactDermatitis(P) -- Plan local Pl, PlT = body.plan.new(Body) local PlL = PlT:append(xml.ELEMENT, 'list') body.plan.pulmonary(Pl, PlL) body.plan.chem7(Pl, PlL) body.plan.teaching(Pl, PlL) body.plan.prednisone(Pl, PlL) body.plan.topical(Pl, PlL) body.plan.followup(Pl, PlL) return cda.serialize(Doc) end function header.document(Doc) local CD = Doc.ClinicalDocument cda.document.addTemplateId(CD, '2.16.840.1.1138126.96.36.1996') cda.document.setId(CD, DOCUMENT_ID, 'c266') cda.document.setCode(CD, '11488-4', 'Consultation note') cda.document.setTitle(CD, 'Good Health Clinic Consultation Note') cda.document.setEffectiveTime(CD, 'April 7, 2000') cda.document.setConfidentialityCode(CD, 'N', 'Normal') cda.document.setSetId(CD, '2.16.840.1.113883.19.7', 'BB35') cda.document.setVersionNumber(CD, 2) end
I could walk through the code and explain everything it does, but the Translator’s annotations can do this much better than I can. I recommend performing the following:
- Create a new channel with the following components:
- ‘From Translator’ source componenet
- ‘To Channel’ destination component
- Download and import these project zip files:
- Explore the code! Step in and out of the functions, inspecting the resulting annotations, and play around with the CDA API modules included in the project.
For a more detailed explanation of how to use our apporach to produce your own CDA documents, check out our comprehensive wiki section covering this topic. This documentation includes a step-by-step tutorial, several templates, and a CDA API guide. If you have any questions, please feel free to contact us!