CDA Creator
The CDA Creator component is used as an example component for creating CDA documents (via the CDA Library) to send to a downstream destination. It demonstrates how to build and map most necessary CDA elements using sample hardcoded values and can be used to build either structured or unstructured CDA documents. It is not designed to parse CDA documents.
Running the Component
STEP 1: Import the CDA Creator component
Using +COMPONENT, import the CDA Creator
STEP 2: Optionally, set a target directory in the component configurations
Set a target directory via the CDADir custom field to change where the generated sample CDA will be stored. If not specified, the file will be created in the Iguana working directory by default.
STEP 3: Optionally, set the component to create the CDA with an unstructured body
To set the component to create a CDA with an unstructured body, enter the Translator via the “Edit” option and edit the script:
-
Comment out mapBody() in line 36 to omit the structured body mapping logic
-
Uncomment mapUnstructuredBody() in line 39 to include the unstructured body mapping logic
STEP 4: Start the component and view the generated CDA
On starting, if successful, the component will display the location of generated CDA:
You can go to the specified location and view the created CDA document.
Adapting the Component
There are two types of changes that need to be made to adapt the component to your workflow:
-
Replace hardcoded values with values mapped from an upstream data source
-
Add or remove elements and attributes
These changes need to be made in the following locations:
CDA XML Template
A standard CDA root template is provided at the top of the script and loaded into the variable CDAtemplate. It contains all the mandatory attributes for a CDA header.
local CDAtemplate = [[
<ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema–instance" xmlns="urn:hl7–org:v3"
xmlns:cda="urn:hl7–org:v3" xmlns:sdtc="urn:hl7–org:sdtc">
<realmCode code="US"/>
<typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
<templateId root="2.16.840.1.113883.10.20.22.1.1"/>
<templateId root="2.16.840.1.113883.10.20.22.1.2"/>
<id extension="" root=""/>
<code/>
<title></title>
<effectiveTime/>
<confidentialityCode/>
<languageCode code=""/>
<setId extension="" root=""/>
<versionNumber value=""/>
</ClinicalDocument>
]]
Based on your requirements, this can be simplified or extended further. If the template needs to be extended, you can move it into a separate Lua file to keep your main.lua easy to read.
This template is used in the main function to generate an XML node tree that allows you to easily add, remove, and modify the CDA document.
local Doc = xml.parse{data=CDAtemplate}
As a guideline, the template should contain what will always be present in your CDA document regardless of what's available in the inbound data.
CDA Header mappings - mapping/mapHeader.lua and mapping/header/*.lua
The mapHeader function acts as the main control function for building and mapping the CDA header. This includes setting values in the header attributes and elements as well as building additional header sections.
In the above example, the additional header sections are created and mapped by the modules in the mapping/header folder.
To customize the CDA header, you will need to:
-
Replace hardcoded values with inbound data in the mapHeader function and individual header mapping modules
-
Add or remove header sections as necessary by creating corresponding mapping modules and loading them into the mapHeader function
CDA Structured Body mappings - mapping/mapBody.lua and mapping/components/*.lua
By default, the component creates a CDA with a structured body. If your interface will continue to create CDA documents with structured bodies, make sure you remove the commented mapUnstructuredBody function call and associated mapping/mapUnstructuredBody.lua.
The mapBody function acts as the main control function for building and mapping the CDA structured body. This includes creating the structuredBody element and building the desired components.
In the above example, each component is created and mapped by the modules in the mapping/components folder.
To customize the CDA structured body, you will need to:
-
Replace hardcoded values with inbound data in the individual component mapping modules
-
Add or remove components as necessary by creating corresponding mapping modules and loading them into the mapBody function
CDA Unstructured Body mappings - mapping/mapUnstructuredbody.lua
By default, the component creates a CDA with a structured body. If your interface needs to create CDA documents with unstructured bodies, make sure you remove the mapBody function call, mapping/mapBody.lua, and associated component mapping modules in mapping/components.
The mapUnstructuredbody function acts as the main control function for building and mapping the CDA unstructured body. This includes creating the nonXMLBody element, setting the appropriate attributes, and loading the sample base64-encoded PDF.
To customize the CDA unstructured body, you will need to:
-
Replace the hardcoded attributes with the appropriate values
-
Replace the hardcoded PDF with the appropriate inbound data
Scaling Mapping Modules
Given the complex structure of CDA documents, it is important that mapping scripts are kept as modular, clear, and repeatable as possible. Let's walk through how we might adapt the current mapVitalSigns.lua to create multiple vital sign entries.
For this example, we'll assume we have a table T of inbound data that contains the specific data we need loaded in the structure we need. In reality, this is not always the case and you may need to load your inbound data into an intermediate table that allows for easier mapping (aka implement the Canonical Data Model design pattern).
Identify what will be repeatable
The first step is to identify what elements will need to be created multiple times. In this example, there may be repetitions at the entry level and at the component level. This is the current hardcoded script for creating the entry:
-- Map entries
local E = S:addElement(’entry’)
local ORG = E:addElement(’organizer’)
CDA.id.addTemplate{target=ORG,
id_type=CDA.codeset.templates["Vital Signs Organizer"]}
CDA.id.add{target=ORG, id_type=’c6f88320–67ad–11db–bd13–0800200c9a66’}
CDA.code.add{target=ORG, element=’code’, system=CDA.codeset.cat.SNOMED_CT,
value=CDA.codeset.snomedCT["Vital signs"], lookup=CDA.codeset.snomedCT.reverse}
CDA.code.addSimple{target=ORG, element=’statusCode’, value=CDA.codeset.status[’Completed’]}
CDA.time.add{target=ORG, element=’effectiveTime’, time=’19991114000000+0500’}
-- Map observations
MapResult(ORG)
ORG:setAttr(’classCode’, ’CLUSTER’):setAttr(’moodCode’, ’EVN’)
E:setAttr(’typeCode’, ’DRIV’)
Create a reusable function
To simplify the mapping process, create a function that that builds the common structure and mappings of repeated elements. In this example, we'll create a function to map an entry with multiple components.
Any mappings that may contain variable information should be handled via the function's input parameters. For example, we can replace the hardcoded statusCode and effectiveTime values with the corresponding values from the inbound data:
CDA.code.addSimple{target=ORG, element=’statusCode’, value=CDA.codeset.status[T.status_code]}
CDA.time.add{target=ORG, element=’effectiveTime’, time=T.effective_time}
We can also use a for loop to dynamically create nested elements or groups of elements based on the available inbound data. In this case, we'll apply this to the existing MapResult function to dynamically create multiple components for this entry:
-- Map observations
for i=1,#T.observations do
MapResult(ORG,T.observations[i])
end
Note: you will also need to update the MapResult function to use the provided input data instead of the sample hardcoded values, but we'll leave it as is for this exercise.
Our resulting entry creation function would look something like this:
local function MapEntry(S,T)
local E = S:addElement(’entry’)
local ORG = E:addElement(’organizer’)
CDA.id.addTemplate{target=ORG,
id_type=CDA.codeset.templates["Vital Signs Organizer"]}
CDA.id.add{target=ORG, id_type=’c6f88320–67ad–11db–bd13–0800200c9a66’}
CDA.code.add{target=ORG, element=’code’, system=CDA.codeset.cat.SNOMED_CT,
value=CDA.codeset.snomedCT["Vital signs"], lookup=CDA.codeset.snomedCT.reverse}
CDA.code.addSimple{target=ORG, element=’statusCode’, value=CDA.codeset.status[T.status_code]}
CDA.time.add{target=ORG, element=’effectiveTime’, time=T.effective_time}
-- Map observations
for i=1,#T.observations do
MapResult(ORG,T.observations[i])
end
ORG:setAttr(’classCode’, ’CLUSTER’):setAttr(’moodCode’, ’EVN’)
E:setAttr(’typeCode’, ’DRIV’)
return E
end
Call your function based on the inbound data
Now that we've created a reusable function for our entry element, we can call it from the main MapVitalSigns function as many times as we need to based on the input data. Assuming T is our inbound data and S is our section element:
for i=1,#T.entries do
MapEntry(S,T.entries[i])
end
Now you've successfully modified the script to create multiple vital sign entries!