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

Using +COMPONENT, import the CDA Creator

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.

image-20240613-170118.png

To set the component to create a CDA with an unstructured body, enter the Translator via the “Edit” option and edit the script:

  1. Comment out mapBody() in line 36 to omit the structured body mapping logic

  2. Uncomment mapUnstructuredBody() in line 39 to include the unstructured body mapping logic

image-20240613-194906.png

On starting, if successful, the component will display the location of generated CDA:

image-20240617-192954.png

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:

  1. Replace hardcoded values with values mapped from an upstream data source

  2. Add or remove elements and attributes

These changes need to be made in the following locations:

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.

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.

image-20240614-155234.png

In the above example, the additional header sections are created and mapped by the modules in the mapping/header folder.

image-20240613-210708.png

 

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

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.

image-20240614-203839.png

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

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.

image-20240614-210924.png

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).

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')

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

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!