Part 1 Discussed the complexities of CCDA transformation. Part 2 demystifies the solution, specifically in the InterSystems HealthShare, Health Connect and IRIS for Health platforms, while Part 3 will help us understand how to streamline the development process for scalability and efficiency.
Let's start with some of our Reference Material:
I'll refer to the documentation linked below throughout this blog:
About SDA:
https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXSDA_ch_about
Healthcare Data -> CDA: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXCDA_ch_cda
Healthcare Data -> IHE:
It's important to note that HealthShare, Health Connect and IRIS for Health all support CCDA transformations although HealthShare is the only product that includes the Unified Care Record (UCR). For the purposes of this article, we'll use the term IRIS to generically cover all of these products unless specific references to UCR are necessary.
Explain It To Me Like I’m Five:
I learned how to work with CCDAs through intense self-study, trial and error, and more than a little hacking - even before today's product documentation had evolved. The current documentation does a great job of calling out specific, relevant details such as where to find the pre-built XSL code and the Annotations.
To pull it all together, we need a framing narrative.
So Here's My Narrative:
In IRIS, there is a CCDA framework that I call the “path of least resistance”. Running through the standard XSLT codebase takes you 85% of the way. Figuring out the remaining 15% can be labor-intensive and time-consuming. Your first goal is to learn the framework and how to feed a CCDA document through the path of least resistance to transform the document from CCDA to SDA.
Why do people say CCDA integrations in IRIS are difficult?
There is no “EASY” button. No single drop point where you can drop a CCDA and just have IRIS automatically gobble it up and transform it just so you can see what happens. BUT -- what is there is SO CLOSE. Implementors must go into a vanilla IRIS install and set up the path of least resistance, by adapting the CCDA framework.
The product domain knowledge required to adapt the framework is not insignificant, but it’s not insurmountable either.
The goal of this post is to make the framework a little easier to understand:
The IRIS CCDA Transformation Framework consists of 3 parts:
The XSL codebase found in /csp/xslt (XSLT)
The HS.Util.XSLTTransformer class (ObjectScript)
Callback utilities - HS.Util.XSLTHelper.cls (ObjectScript)
The flow for the path of least resistance is this:
Step 1 Pre-Processor: An optional, custom step to map inbound CCDA to conform to the expectations of the code base. Generally, minor changes would be made here to the inbound CCDA using XSLT to normalize it.
Step 2 Base Transform: The normalized CCDA is passed to the standard code base through one of these XSL transforms:
CCDAv21-to-SDA.xsl
CCDA-to-SDA.xsl
CDA-to-SDA.xsl
The above list is not exhaustive, but we must walk before we run
Step 3 Post-Processor: An optional step to modify the SDA to make additional modifications before further handling. After this step, the SDA may be sent to an Edge and ingested into UCR if you are working in a HealthShare environment. Alternatively, you can implement pre-built or custom transformations to convert SDA to other output formats (i.e., FHIR, HL7).
Running just the base transform on the CCDA will get you most of the way there. The data looks almost complete. Small tweaks will get you the rest of the way.
Pitfalls: If the base transformation errors out and/or the CCDA requires extensive troubleshooting, in-depth domain knowledge and experience may be needed, making the level of effort for CCDA transformation hard to gauge from the onset.
There are parts of the process that are rightfully difficult. But because of the complexity of the setup, the entire thing can feel like a dark void, until we deconstruct and demystify the process.
The Five Things You Need to Know to Transform CCDs in IRIS:
1. What are the default transformations and where do I find them?
The XSLT code base is found in the CSP folder (this is outside of the Caché ObjectScript codebase). The “csp/xslt” folder location is typically mapped via Web Application to the HSCUSTOM or HSLIB namespace in HealthShare.
If this has not been done already, you will need to configure the Web Application through the System Management Portal before you can view the XSLT files in IRIS Studio or Visual Studio Code (VSCode).
In IRIS Studio:
You can find them in Studio if you select the “Namespace” tab and then open “CSP Files” in the project navigation panel.
In VSCode:
Select “InterSystems Server Manager” application extension. Navigate to your install server. My example shows a local install called “localhost”. Go to HSLIB/Web Applications/csp/xslt and select the pencil icon.
Once you’ve added the location to your workspace, go to Explorer by selecting the icon with the two files on the left navigation bar in VSCode. You’ll see the HSLIB web files location which will allow you to navigate and view the XSL files.
What am I supposed to pay attention to?
This is the XSLT code base. It contains more than just CCDA transforms and it's a firehose of information. One day, you may come to walk the length and breadth of this library, but for now, just know it’s there and focus on the common CCDA transformation entry points.
The SDA3 folder is where you’ll find XSL code for transforming various formats into the SDA data model. There is one top-level XSL transform for each version of the CCDA. Pass any one of these into the XSLT Transformer class and it will run and call all the downstream XSL mappings for the various sections and fields.
Here is an unofficial explanation of what you should pay attention to:
CCDAv21-to-SDA.xsl (CCDA version 2.1 – the latest and greatest)
CCDA-to-SDA.xsl (CCDA version 2.0, version 1.1. This is my go to. It will map most of version 2.1 pretty well and is more consistent)
CDA-to-SDA.xsl (old school CCD version 1.0.)
2. What is the minimal, most barest of bones way to get a CCDA transformation working in a business process?
The HS.Util.XSLTTransformer class provides the ObjectScript hook into the XSL codebase.
Set transformer = ##class(HS.Util.XSLTTransformer).%New()
$$$ThrowOnError(transformer.Transform(inputCDAStream, “SDA3/CCDA-to-SDA.xsl”, .transformedCda, .params))
Here’s an example where the XSL code is loaded from a stream versus supplying the file location:
// Do the XSL Transform
Set tStatus = ##class(Ens.Util.URLStream).GetURLStream("xdata://" $THIS ":CCDA-to-SDA.xsl", .tCCDATransform)
$$$ThrowOnError(tStatus)
Set tStatus = ##class(%XML.XSLT.CompiledStyleSheet).CreateFromStream(tCCDATransform, .tCompiledStyleSheet)
$$$ThrowOnError(tStatus)
Set tStatus = ##class(%XML.XSLT.Transformer).TransformStreamWithCompiledXSL(pStream, tCompiledStyleSheet, .pOutputStream,, .tParams, ##class(HS.Util.XSLTHelper).%New())
$$$ThrowOnError(tStatus)
Here the transform is called with the method TransformStreamWithCompiledXSL which is a version of the transform method that takes in a compiled stylesheet object rather than a file location as a String.
The extra parameter after .tParams: ##class(HS.Util.XSLTHelper).%New() instantiates a Callback Processor and passes a default version of that. More on this in #5 below.
3. How do I run the transform? Where does the transformation process run?
If you are running HealthShare, the easiest way to turn on CCDA transformation or consumption is within an Edge Gateway. This assumes knowledge of IHE pipelines, an area I am largely handwaving for now.
If you have an Edge Gateway set up for IHE/XDSb integration, you’re in luck!
Go to the Edge Gateway production. Add this Business Operation: HS.IHE.XDSb.ContentConsumer.Operations.
In the IHE pipeline for processing Provide and Register transactions, there is typically a HS.IHE.XDSb.Repository.Operations business operation which registers the CCDA document to the repository. Set the Content Consumer operation to “HS.IHE.XDSb.ContentConsumer.Operations”.
Documents sent to the repository will now be automatically forwarded for CCDA transformation and consumption into SDA (and subsequently the underlying streamlets for Patient data).
If not using the Content Consumer, you will need to create a custom Business Process or Business Operation with the HS.Util.XSLTransformer code.
4. What if I need to customize a pre-built mapping?
The product documentation does discuss how to do this in detail. (https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXCDA_ch_custom_transform#HXCDA_create_custom)
Rather than modifying the existing code base, create a custom version of the template and load it “on top” of the code base. If the custom code is loaded after the base, the custom version will override the base version.
This image illustrates the process:
The above example shows what the Custom folder may contain for a transformation that required a customization to CCDA-to-SDA.xsl and a change to the Medications.xsl.
A copy of CCDA-to-SDA.xsl is created in Custom. Note how there is also a custom Medications.xsl in a location that mirrors the standard SDA3 directory structure.
At the top of the XSL, include statements load the standard CCDA-to-SDA.xsl file. Then any custom XSLs, like the Medications.xsl, are included as well. The effect is that the standard template definitions are loaded first. Then any custom definitions.
If there are any updates to templates from CCDA-to-SDA.xsl, then the definition in this file will overwrite the standard version. In this example, the Allergies section was commented out so that Allergies will not be mapped into the SDA. When calling the CCDA Transform method, this updated XSL in Custom should be referenced in order to use the customized transforms.
5. How can I leverage utilities written in ObjectScript?
External utilities can be defined in a callback handler class.
The default callback handler is located in class: HS.Util.XSLTHelper.cls
A default handler class consists of two key elements:
An “evaluate” method that takes in a variable length list of arguments. Within the evaluate method, there are references pointing to all the available helper methods. The references set up how the helper methods are invoked from XSL.
Helper methods written in ObjectScript.
The Callback Handler class is set as a property of the HS.Util.XSLTTransformer class. If not explicitly set, the default HS.Util.XSLTHelper.cls is used. Some Transform methods allow the callback handler class to be passed in. Transform methods that don’t take in the callback handler as an argument will typically reference the property downstream. Hiding this from our eyes sometimes, but not all the time, can make the process more confusing, but now you know…
The methods can then be called from with the XSL using the isc-evaluate syntax.
Example:
<!-- Replace AEHR OID in sourcePatientId with NSMG OID -->
<xsl:template match="//rim:Slot[@name='sourcePatientId']/rim:ValueList">
<xsl:variable name="tValue">
<!-- Search and replace AEHR OID with NSMG facility OID -->
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="replace" select="isc:evaluate('CodetoOID', 'AEHR')" />
<xsl:with-param name="with" select="isc:evaluate('CodetoOID', 'NSMG')"/>
</xsl:call-template>
</xsl:variable>
<xsl:copy>
<xsl:element name="rim:Value">
<xsl:value-of select="normalize-space($tValue)"/>
</xsl:element>
</xsl:copy>
</xsl:template>
This template makes two calls to the method referenced by the argument value of “CodetoOID” in the callback handler class (HS.Util.XSLTHelper.cls)
How Do I Get Started Transforming CCDAs into HealthShare?
Here’s what I suggest to CCDA beginners:
Browse through the materials in this blog to familiarize yourself with the components needed.
Take a tutorial on XPATH and XSLT.
Create a tester class in ObjectScript to set up the components and use HS.Util.XSLTTransformer to call one of the base XSL transforms directly.
Test by passing in a CCDA to get an SDA result. Compare the before and after.
Write a pre-processor XSL to transform CCDA to CCDA, changing ONE thing. Compare the before and the after.
Now that we’ve deconstructed how to work with XSLT transformations through the path of least resistance, the next question is how do we address the gaps and challenges to make the development process scalable and developer-friendly?
In Part 3, I will discuss how to streamline the CCDA analysis, testing and debugging process. I will also share proven methods for streamlining development and tools and utilities designed to fast-track developers into the world of CCDA in IRIS.
Additional References:
How do I find out the CCDA version?
Comments