Wednesday, September 16, 2020

Generating your own FHIR Narrative with XML using SUSHI

Building an implementation guide relies on a stack of transformations.  It's a good thing we know how to stack things up on top of each other these days.  But what happens when something in the stack isn't quite adequate?  How do you manipulate things to make it all work.

This example a particularly interesting challenge:  I want to be able to generate my own Narrative from the XML version of the FHIR Resource.  I won't have the XML when SUSHI is done, only after the IG Builder completes.  I can easily get the XML Version from the JSON version by running this little program:

    public static void main(String args[]) {
        IParser op = null;
        IParser ip = null;
        for (String arg: args) {
            IBaseResource r;
            File fin = new File(arg);
            File fout;
            if (arg.endsWith(".json")) {
                fout = new File(fin.getParent(), fin.getName().replace(".json", ".xml"));
                op = xp;
                ip = jp;
            } else if (arg.endsWith(".xml")) {
                fout = new File(fin.getParent(), fin.getName().replace(".xml", ".json"));
                op = jp;
                ip = xp;
            } else {
                System.err.println("Do not know how to convert " + arg);

            try (FileReader fr = new FileReader(fin); FileWriter fw = new FileWriter(fout)) {
                r = ip.parseResource(fr);
                op.encodeResourceToWriter(r, fw);
            } catch (IOException e) {
                System.err.printf("Cannot convert %s: %s\n", arg, e.getMessage());

But then, how do I insert my narrative into the build process?  If it has to happen
post-SUSHI processing, then IG-builder won't execute it.  I'm only doing this for ONE resource (or perhaps a few), not the 97 or so that I otherwise generate using SUSHI.  It will get infrequently updated, and I don't want to give up the convenience of having the automated SUSHI run in IG Publisher do most of the work for me.  

But hey, if you can make your transform stack run in a circle the way the Crazy Russian does with Pringles, you can make it work.

Here's what I decided to do:

In the Resource that I want generated text, I include the following:

Instance: ComputableCDCPatientImpactAndHospitalCapacity
InstanceOf: PublicHealthMeasure
Title: "Computable CDC Patient Impact and Hospital Capacity"
* insert ComputableCDCPatientImpactAndHospitalCapacityText

Then I create a RuleSet named ComputableCDCPatientImpactAndHospitalCapacityText and modify my XSLT to generate that ruleset instead of content of the DIV.  The template to generate the ruleset looks like this:

<xsl:template match="/">
    <xsl:text>RuleSet: </xsl:text><xsl:value-of select="fhir:Measure/fhir:id/@value"/><xsl:text>Text&#xA;</xsl:text>
    <xsl:text>* text.status = #generated&#xA;</xsl:text>
    <xsl:text>* text.div = """&#xA;</xsl:text>
    <div xmlns="">
        <xsl:apply-templates select="/fhir:Measure"/>

Finally, my process to update the generated narrative is to do the following:

  1. Generate the resources using SUSHI
  2. Run my XSLT to generate the Narrative
  3. Rerun the IG Publisher
Basically, I'm using SUSHI to generate an input to a second run SUSHI (run by the IG Publisher).

I'm not happy with this.  There should be a way to insert this generation step somewhere else in the tool chain, and update the tool chain (perhaps with templates) so that I don't have to rely on the FHIR Generated narrative, but can rather build my own from the XML resource.

It's a bit uglier than that even, because some of the resource content (e.g., description, definition, et cetera) is actually Markdown, rather than HTML, and I had originally used markdown tags to support bulleted lists or definition lists and links in some of that content where appropriate.  I punted on that problem by simply replacing that limited markup with its (again limited) HTML equivalents since HTML markup is allowed in Markdown.


Post a Comment