Convert your FHIR JSON -> XML and back here. The CDA Book is sometimes listed for Kindle here and it is also SHIPPING from Amazon! See here for Errata.

Monday, October 9, 2017

Where do I find the Medication Generic Name in a CCD Document

The answer is, it depends on your CCD version:

CCDA 2.1 has this to say:
 4. SHALL contain exactly one [1..1] manufacturedMaterial (CONF:1098-7411).
     Note: A medication should be recorded as a pre-coordinated ingredient + strength + dose form (e.g., “metoprolol 25mg tablet”, “amoxicillin 400mg/5mL suspension”) where possible. This includes RxNorm codes whose Term Type is SCD (semantic clinical drug), SBD (semantic brand drug), GPCK (generic pack), BPCK (brand pack).
     1. This manufacturedMaterial SHALL contain exactly one [1..1] code, which SHALL be selected from ValueSet Medication Clinical Drug urn:oid:2.16.840.1.113762.1.4.1010.4 DYNAMIC (CONF:1098-7412).
         1. This code MAY contain zero or more [0..*] translation, which MAY be selected from ValueSet Clinical Substance urn:oid:2.16.840.1.113762.1.4.1010.2 DYNAMIC (CONF:1098-31884).

CCDA 1.1 has this to say:
 4. SHALL contain exactly one [1..1] manufacturedMaterial (CONF:81-7411).
     Note: A medication should be recorded as a pre-coordinated ingredient + strength + dose form (e.g., “metoprolol 25mg tablet”, “amoxicillin 400mg/5mL suspension”) where possible. This includes RxNorm codes whose Term Type is SCD (semantic clinical drug), SBD (semantic brand drug), GPCK (generic pack), BPCK (brand pack).
     1. This manufacturedMaterial SHALL contain exactly one [1..1] code, which SHALL be selected from ValueSet Medication Clinical Drug urn:oid:2.16.840.1.113762.1.4.1010.4 DYNAMIC (CONF:81-7412).
         1. This code SHOULD contain zero or one [0..1] originalText (CONF:81-7413).
             1. The originalText, if present, SHOULD contain zero or one [0..1] reference (CONF:81-15986).
                 1. The reference, if present, SHOULD contain zero or one [0..1] @value (CONF:81-15987).
                     1. This reference/@value SHALL begin with a '#' and SHALL point to its corresponding narrative (using the approach defined in CDA Release 2, section 4.3.5.1) (CONF:81-15988).
         2. This code MAY contain zero or more [0..*] translation (CONF:81-7414).
             1. Translations can be used to represent generic product name, packaged product code, etc (CONF:81-16875).

HITSP C32 has this to say (you can actually find this in the HITSP C83 specification):
2.2.2.8.13 Free Text Product Name Constraints
C83-[DE-8.15-CDA-1] The product (generic) name SHALL appear in the <originalText> element beneath the <code>

It's pretty clear that the preferred way to handle this changed in between CCD 1.0 (HITSP C32) and CCDA 1.1, and also that some critical information loss occurred with regard to how to record generic name between CCDA 1.1 and 2.1.  I think as industry understanding of CDA expanded, the need to express the detail about generic name probably changed, but not necessarily for the better.

If you want to include generic name, you would do it in a translation -- when you don't already list the drug using an RxNORM code from the Semantic Clinical Drug value set (generic codes) (e.g., you use a Semantic Branded Drug code).

I have two statements to make about this:

  1. Not all implementers are informaticists or would understand the distinction between types of RxNorm codes.  We (HL7) need to remember to speak to who is doing the work, not to ourselves.
  2. Brand and Generic information is already represented as relationships embedded in the RxNorm terminology itself.  The simultaneous transmission of a brand code and a generic code for that same drug simply repeats what is already present in RxNorm.  The advice I give these days would be to trust RxNorm before you trust your trading partner, and if what your trading partner tells you CONFLICTS, someone needs to go raise a red flag about inconsistent data.

   Keith



Wednesday, September 27, 2017

Security and Privacy: Where are we headed

So, the new iPhone's are here, along with new security features.  Combine that with this recent bit in my inbox and I have a few predictions.

A study published in Healthcare Informatics Research finds 73 percent of medical professionals have used another staff member's password to access a patient's electronic health record at work, HealthITSecurity reports.


Facial recognition, will be used to solve for this problem.  Patient safety advocates will jump in to take advantage of the technology, which will be followed shortly thereafter by the computer saying, you look tired, are you sure you should be caring for patients …

At some point in time, this will move into the commercial domain (e.g., software developers, others creating IP).  It will expand into eavesdropping protection, which will lead to DOS attacks by small children popping their heads up in the seat behind you while you are trying to get work done on the plane or train or subway.

At some point at an IHE Connecthon, all testing work will stop as we all have to get exceptions to have competitors in the same room with our code, but cannot complete the process with them standing too close. This will lead to an eventual revolt against security and privacy altogether as similar challenges pop up across the business spectrum.

Eventually we will give up altogether on having any sort of privacy or security, and the world will live peacefully together.

   Keith

P.S. And then the aliens come and wipe us all out because we couldn't even hide from them properly.

Restricted Includes

Call me stupid. I spent the last 12 hours working on a performance challenge before I realized what the real solution was.  The issue was that I was using a FHIR _include parameter on an existing query to get included resources that needed to be displayed.  The performance was absolutely miserable.

To explain a bit, MedicationStatement and MedicationOrder reflect two different sides of an intention that a patient be given or taking certain medications.  The MedicationStatement resource is (quoting DSTU2):

A record of a medication that is being consumed by a patient. A MedicationStatement may indicate that the patient may be taking the medication now, or has taken the medication in the past or will be taking the medication in the future. The source of this information can be the patient, significant other (such as a family member or spouse), or a clinician.

Whereas MedicationOrder is:

An order for both supply of the medication and the instructions for administration of the medication to a patient. 

And while neither MedicationOrder nor MedicationStatement reference each other, the MedicationStatement does provide for "supportingInformation" as a Reference to any resource.  I wanted to link the two to show the physician intention with the actual prescriptions and refills given.

But then when querying for MedicationStatement for a time period, I also wanted MedicationOrder, so I just grabbed the included references.  Needless to say, this was a MISTAKE, because a patient may have been taking a medication for years and had literally hundreds of refills (I'm not kidding here 3 years of monthly refills on three meds is > 100, and hell, that could even be me).

The first sign of this was some icky performance.  But see, the MedicationOrder stuff is there not because I have an immediate use for it, but rather because I'm following the CCD/CCDA pattern long established, and I KNOW it will be used in something I have to work with downstream, so I included it.  So, it is kinda hidden and took a while to track down.  AND then I spent about 8 hours trying to improve the performance of the MedicationOrder retrieval instead of asking about the quantity of data.

It might have been advantageous to go after MedicationOrder in the _include because of my data model and processing flow, but FHIR syntax for queries don't cross into _included resource in DSTU2 (I get to play with STU3 soon, maybe they've solved the problem there).  I cannot in DSTU2 say: Give me these MedicationStatement resources, with ONLY the _included MedicationOrder resources that look like that.  Yeah, I'm sure I could use the extended query syntax to get to this, but I'm looking for a bit more elegance here (that's what engineers call complexity that looks cool).

So, here's my thought on syntax:

_include=MedicationStatement:supportingInformation:MedicationOrder(setName)&_restrict:setName.searchParam=value

This would name the set of included fields, AND allow me to set an inclusion restriction on them.
If we only had that, AND if I implemented it, my problem would be solved.  A simple matter of programming, what? Yeah.

Nah.  FHIR Query syntax is complicated enough.  But here is a use case for something we haven't thought of and the nice thing about it is that it seems simple enough to understand (even if I don't yet really know how to implement it).  Is it in the 80%?  Maybe.  I have ONE use case for this.  I could probably find others.  I'm not going to spend much more time on this, I still have to fix that performance problem now that I've found it.

   Keith





Monday, September 25, 2017

In my Inbox

This morning I received a long not necessarily relevant announcement to email list I don't remember subscribing to, followed by 30 replies. The replies are all from relatively educated people, many of whom know better, and are summarized below for your reading amusement:

R1: Please remove me from this list
R2: Hi R2, R3 did not send this to you ...
R3: I am not R2
R4: Please respond to the person/s directly and not send a reply to all
R5: Please remove me from all future emails concerning this program
R6: I find reply all useful when unsure who the admin is.
R7: Must you use "reply to all"
R8: Meme "Reply All"
R9: For God's sake everybody -- quit hitting 'reply all' ...
R10: Please remove me as well.
R11: The same here.
R12: This is officially ridiculous. Can everyone stop replying to all these emails?
R13: Same
R14: I don’t know what this email is either and I certainly did not send it out. Please remove me as well.
R15: Hitting reply on the original message only sends the message to the person who sent the email which should be the admin of the list.
R8: Good luck, R3! Keep me posted on the outcome.
R17: Please remove me from your list...
R8: Who's on first?
R20: You guys realize by replying all and asking people to stop replying all that you're just part of the problem, right?...
R21: I just became an Ohio State fan…
R22: I don’t know why I am on this list, so please remove me as well, whoever the admin is.
R23: And good Lord, people, there’s a contact email in the body of the original message:______
Although I must say this has been highly entertaining and a big improvement over the typical Monday.
R24: Please remove me from this list.
R25: Please remove me from this list. Thank you!
R26: Dear whomever, I already have <degree>.  I need <job>...
R27: 
R28: Me too (in reply to me too).
R29: It appears the original email came from ____. Please direct your request to her alone...
R30: Sorry R?, but hitting reply to all just fills our inboxes with garbage.

    ... and still going ...

P.S. My e-mail is simply going to point to this blog post and ask everyone to comment here.

Monday, September 18, 2017

Comparing Dynamically Generated Documents

Sometimes to see if two things are similar, you have to ignore some of the finer details.  When applications dynamically generate CDA or FHIR output, a lot of details are necessary, but you cannot control always control all the values.  So, you need to ignore the detail to see the important things.  Is there a problem here?  Ignore the suits, look at the guns.

Creating unit tests against a baseline XML can be difficult because of detail. What you can do in these cases is remove the stuff that doesn't matter, and enforce some rigor on other stuff in ways you control rather than your XML parser, transformer or generation infrastructure.

The stylesheet below is an example of just such a tool.  If you run it over your CDA document, it will do a few things:

  1. Remove some content (such as the document id and effective time) which are usually unique and dynamically determined.
  2. Clean up ID attributes such that every ID attribute is numbered in document order in the format ID-1. 
  3. Ensure that internal references to those ID attributes still point to the thing that they originally did.

This stylesheet uses the identity transformation with some little tweaks to "clean up" the things we don't care to compare.  It's a pretty simple tool so I won't go into great detail about how to use it.

   Keith


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cda="urn:hl7-org:v3">
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match='@ID'>
    <xsl:attribute name="ID">
      <xsl:text>ID-</xsl:text>
      <xsl:number count='//*[@ID]'/>
    </xsl:attribute>
  </xsl:template>
  
  <xsl:template match='/cda:ClinicalDocument/cda:id|/cda:ClinicalDocument/cda:effectiveTime|/cda:ClinicalDocument/cda:*/cda:time'>
    <xsl:copy>Ignored for Comparison</xsl:copy>
  </xsl:template>
  
  <xsl:template match="cda:reference/@value[starts-with(.,'#')]">
    <xsl:attribute name="value">
      <xsl:text>#ID-</xsl:text>
      <xsl:value-of select='count(//*[@ID=substring-after(current(),"#")]/preceding::*/@ID)+1'/>
    </xsl:attribute>
  </xsl:template>
  
  <xsl:template match='@ID' mode='count'>
    <xsl:attribute name="ID">
      <xsl:text>#ID-</xsl:text>
      <xsl:number count='//*[@ID]'/>
    </xsl:attribute>
  </xsl:template>
  
</xsl:stylesheet>


Wednesday, September 13, 2017

Matt the Mighty, A PrecisionMedicine Super Hero (Dad)

Every year in September, HL7 has its "Plenary" session. This is a half day where we hear from folks outside of the working groups on important topics related to what we do.

This year we heard from Matt Might, whom I now would christen Matt the Mighty for his Super-Dad precision medicine powers.  Either that, or as close in real life as one could come to a Doctor McCoy.

You really have to hear him tell the whole story because A) He is an awesome story teller, and B) there's simply so much more depth to it.

The long and short of it though, is not only does he help to figure out how to identify a rare (n=1?) disease, and develop a diagnostic test for it, and identify other possible sufferers, but also a treatment (not a complete cure,  but addressing some effects) among already FDA approved substances (lucking out on an OTC drug), and develops model legislation that his state passes to allow for "Right to Try" use of medications for these cases, and builds a process by which other n=1? disease patients can benefit from it, starting with his own son.

That's Mighty powerful application of precision medicine (pun fully intended).  If you weren't here, I'm sorry you missed it, and urge you to listen to him speak elsewhere.

   Keith

Thursday, September 7, 2017

Demand Driven Pricing

One of the things we've seen from early warnings about Hurricane Irma is a significant increase in prices in airline fares from some airlines.  Some of this, I'm sure is due to automated pricing algorithms on fares based on demand, for which there may very well be little or no human intervention.

That got me to thinking about how demand driven pricing AND demand driven reimbursement could have an interesting impact on prices for healthcare services IF it were possible to apply them more interactively and faster.

In the battle of algorithms, the organization with the best data would most likely win.  I see four facets to that evaluation of "Best": Breadth, Expression, Savvy, and Treatment (see what I did there?).

  • Breadth
    More bigger data is better.
  • Expression
    If your data is organized in a way that makes correlations more obvious, then you can gain an advantage.
  • Savvy
    If you know how A relates to B, you also gain an advantage.  Organization is related to comprehension.
  • Treatment
    Can you execute?  Does the data sing to you, or do you have to filter signal from a vast collection of white noise?
In the 5P model of healthcare system stakeholders, Polity (Government), Payer, Provider, Patient, and Proprietor (Employers):
  1. Who has the largest breadth of data? The smallest?
  2. Who has the best expression of data? The worst?
  3. Who has the greatest savvy for the data? The least?
  4. Who will be most able to treat the data to their best advantage? The least?

It seems pretty clear that the patient has the short end of the stick on most of this, except perhaps on their "personal" collection of data.

Payers are probably in better shape than others with regard to breadth, followed closely by Polity. The reason I say that is because government data is dispersed ... the left hand and the right hand can barely touch in some places.  Providers rarely have the breadth unless they begin to take on the Payer role as well (e.g., Kaiser, Intermountain, et cetera).

Providers have a better chance of having better expression, being able to tie treatment to condition in more detail, and have some chance at understanding outcomes as well.

It's not clear that employers are THAT much better off than patients, although frankly I honestly don't know how much information they really have.

Treatment is where it all comes together, and right now in the US, it seems that nobody has yet found the right treatment ...

Anyway, it's an interesting place to explore further.

   Keith