Category Archives: v3

Question: IHE PIX v3 Acknowledgement

Question

I am working on a PIX v3 feed implementation. Have taken pains to understand the v3 messaging structure. I have been following IHE norms for PIXv3. Currently am working on sample AcceptAck message. The sample message that I created looks like this:

<MCCI_IN000002UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="urn:hl7-org:v3">   
  <id extension="34236" root="2.16.578.1.34.1.871.3"/>  
  <creationTime value="20080607154128"/>   
  <versionCode code="NE2008"/>
  <interactionId extension="MCCI_IN000002UV01" root="2.16.840.1.113883.1.6"/>
  <processingCode code="P"/>
  <processingModeCode code="T"/>
  <acceptAckCode code="NE"/>
  <receiver typeCode="RCV">
    <device classCode="DEV" determinercode="INSTANCE">
      <id extension="408" root="2.16.578.1.34.1"/>
    </device>
  </receiver>       
  <sender typeCode="SND">
    <device classCode="DEV" determinerCode="INSTANCE">
      <id extension="871" root="2.16.578.1.34.1"/>
    </device>
  </sender>
  <acknowledgement>
    <typeCode code="AE"/>
    <targetMessage>
      <id extension="0806071541103" root="2.16.578.1.34.1.408.7"/>
    </targetMessage>
    <acknowledgementDetail>
      <text>Record not found.</text>
    </acknowledgementDetail>
  </acknowledgement>
</MCCI_IN000002UV01>

Is something missing in this message which is mandatory? Do we need to add Agent and Organization component in this ack message? IHE has mentioned them but not made clear as to whether they are mandatory or not? Can you shed light on this aspect?

Answer:

Well, this is taken from the standard HL7 v3 transmission subject, found here (if you are not a registered user of HL7.org, you’ll get a link saying you must be a paid user to get that content, but you only have to sign up, not pay).

The relevant content description is best summarised in the applicable RMIM design:

MCCI_RM000200UV

 

 

Everything that this says is required is populated in the message, so it’s valid as far as HL7 is concerned. As shown in this diagram, the Agent and Organization parts are optional.

Nothing in the IHE documentation tells me that these are required, and I can’t think of any valid reason for them to be required for PIX/PDQ.  So it looks fine to me.

Question: Act-relationship for Pathology Dissection

Question

Pathology resident performs a gross dissection examination of the tissue submitted from a surgical procedure performed by a doctor to remove a patient’s gallbladder.

What is the ActRelationship?

Answer

Well, that’s kind of difficult. If I was doing this for real, I’d ask a lot of questions, but here’s a tentative outline:

  • Pathology Resident (role/entity)
  • performs (Participation type = performs)  
  • a gross dissection examination (Procedure [1])
  • of the tissue (role/entity participating as subject or specimen)
  • submitted (act-relationship from specimen role, typeCode =product)  
  • from a surgical procedure (Procedure [2])
  • performed by a doctor (performer to procedure [2])
  • to remove a patient’s (subject of procedure [2])
  • gallbladder (targetSiteCode of procedure [2])

That shouldn’t be taken as final. Some variations of this are possible, depending on the answer to the questions. (e.g. I swept the submission under the carpet in this outline. I don’t know whether the act of submission is interesting and/or relevant or not)

Guest Post: HL7 Language Codes

This guest post is written by Lloyd McKenzie.  I’ve been meaning to get to it since the January WGM, but I’ve been wrapped up in other things (most recently HIMSS).  However, I agree with what Lloyd says.

Question: I have a need to communicate a wide variety of language codes in HL7 v3 instances, but the ISO Data Type 21090 specification declares that ED.language (and thus ST.language) are constrained to IETF 3066.  This is missing many of the languages found in ISO 639-3 – which I need.  Also, IETF 3066 is deprecated.  It’s been replaced twice.  Can I just use ISO 639-3 instead?

Answer:

The language in the 21090 specification was poorly chosen.  It explicitly says “Valid codes are taken from the IETF RFC 3066”.  What it should have said is “Valid codes are taken from IETF language tags, the most recent version of which at the time of this publication is IETF RFC 3066”.  (Actually, by the time ISO 21090 actually got approved, the most recent RFC was 4646, but we’ll ignore that for now.)  This should be handled as a technical correction, though that’s not terribly easy to do.  However, implementers are certainly welcome to point to this blog as an authoritative source of guidance on ISO 21090 implementation and make use of any language codes supported in subsequent versions of the IETF Language Tags code system – including RFC 4646 and RFC 5646 as well as any subsequent version there-of.

The RFC 5646 version incorporates all of the languages found in ISO 639-3 and 639-5.  However, be aware that while all languages are covered, there are constraints on the codes that can be used for a given language.  Specifically, if a language is represented in ISO 639-1 (2-character codes), that form must be used.  The 3-character variants found in ISO 639-2 cannot be used.  For example, you must send “en” for English, not “eng”.

Question: But I want to send the 3-character codes.  That’s what my system stores.  Can’t I use ISO 639-2 directly?

Answer:

No.  In the ISO 21090 specification, the “language” property is defined as a CS.  That means the data type is fixed to a single code system.  The code system used is IETF Language Tags, which is consistent with what the w3c uses for language in all of their specifications and encompasses all languages in all of the ISO 639 specifications plus many others (for example, country-specific dialects as well as additional language tags maintained by IANA.)

Question: Well, ok, but what about in the RIM for LanguageCommunication.code.  Can I send ISO 639-2 codes there?

Answer:

Yes, though with a caveat.  LanguageCommunication.code is defined as a CD, meaning you can send multiple codes – one primary code and as many translations as you see fit.  You are free to send ISO 639-2 codes (the 3-character ones) or any other codes as a translation.  However, LanguageCommunication.code has a vocabulary assertion of the HumanLanguage concept domain, which is universally bound to a value set defined as “all codes from the ietf3066 code system”.  That means the primary code within the CD must be an IETF code.  So that gives you two options:

  1. Fill the root code with the appropriate IETF code – copying the ISO code most of the time and translating the 3-character code to the correct 2-character code for those 84 language codes found in ISO 639-1; or
  2. Omit the root code property and set the null flavor to “UNC” (unencoded), essentially declaring that you haven’t bothered to try translating the code you captured into the required code sytem.

And before you mention it, yes, the reference to IETF 3066 is a problem.  The actual code system name in the HL7 specification is “Tags for the Identification of Languages”, which is the correct name.  However the short name assigned was “ietf3066” and the description in the OID registry refers explicitly to the 3066 version.  This is an error, as IETF 3066 is a version of the IETF “Tags for the Identification of Language” code system and the OID is for the code system, not a particular version of it.  (There have actually been 4 versions so far – 1766, 3066, 4646 and 5646.)  We’ll try to get the short name and description corrected via the HL7 harmonization process

Question: But I don’t want to translate to 2-character codes and I don’t want to use a null flavor.  Can’t we just relax the universal binding?

Answer:

We can’t relax the binding because the HumanLanguage concept domain is shared by both the ED.language property in the abstract data types specification (which ISO 21090 is based on) and the LanguageCommunication.code attribute.  The ED.language is a CS and so must remain universally bound.

In theory, we could split into two separate domains – one for data types and one for LanguageCommunication.code.  The second one could have a looser binding.  However, it’s hard to make a case for doing that.  There are several issues:

First, having two different bindings for essentially the same sort of information is just going to cause grief for implementers.  You could be faced with declaring what language the patient reads in one code system, but identifying the language of the documentation the patient’s supposed to read in a second code system.

Second, the IETF code system fully encompasses all languages covered by all the ISO 639-x code systems, plus thousands of others expressible using various sub-tags such as identifying country-specific dialects.  In the unlikely situation that you need a language that can’t be expressed using any of those, there’s even a syntax for sending local codes (and a mechanism for registering supplemental codes with IANA if you want to be more official).  So there should never be a situation where you can’t express your desired language using the IETF Language Tags code system.

Question: I don’t really care that I can express my languages in IETF.  I’ve already pre-adopted using ISO 639-2 and -3 in my v3 implementation and I don’t want to change.  Why are you putting constraints in place that prevent implementers from doing what they want to do?

Answer:

Well, technically your implementation right now is non-conformant.  And implementers always have the right to be non-conformant.  HL7 doesn’t require anyone to follow any of its specifications.  So long as your communication partners are willing to do what you want to do, anything goes by site-specific agreement.

However, the standards process is about giving up a degree of implementation flexibility in exchange for greater interoperability.  By standardizing on a single set of codes for human language, we’re able to ensure interoperability across all systems.  Natively, those systems may use other code systems, but for communication purposes, they translate to the common code system so everyone can safely exchange information.

If the premise for loosening a standard was “we won’t require any system to translate data from their native syntax”, there’d be no standards at all.  Yes, translation and mapping requires extra effort (though a look-up table for 84 codes with direct 1..1 correspondence is pretty easy compared to a lot of the mapping effort needed in other areas.)  But that’s the price of interoperability.

Do I need to render the CDA Narrative?

In a couple of different CDA implementation contexts, the same question has come up: does an application receiving the CDA document need to render the document using the narrative, or can it simply ignore the narrative and process and display the structured data? If it can, under what conditions is that OK?

As a background, CDA is a document that has a narrative presentation, a header that defines the clinical document, and which may include some structured data. By the original intent of CDA, it’s very much a document, and should be thought of as a standard way of doing a word document with embedded data, like this example from my usual CDA tutorial:

cda-as-a-document

So ignoring the narrative is inherently a non-CDA type of thing to do. The anticipation in the CDA specification itself is that it’s alright to extract the data from the CDA document for other use, but once you’ve done that, it’s no longer part of the attested content of the document. The implication is that whenever you extract data, you should always retain a link to the source document, so that a user can see the original data in it’s context. For instance, the source system/clinician may have noted some qualifying information about the data in the narrative that is relevant to it’s interpretation.

However CDA is used in all sorts of contexts, some of them extremely data-centric. In practice, there are some uses where the document is pure human-written narrative, some where the CDA documents are pure data, and the constructed narrative is only a formality to satisfy the CDA specification itself, and others that are a mix, both in terms of implementations being more or less data driven, and different parts of the documents using different combinations. In some of these uses, it’s safe and even normal to ignore the narrative.

Given the diagram above, when is it ok to ignore the narrative? When:

  • The authoring application populates the structured data completely and knows that the narrative says nothing additional
  • The receiving application is able to correctly process all the structured data
  • The receiving application is able to know the correct way to display the data

So it’s a collaborative effort between the author and the receiver.

Given the wide flexibility of the entries, and the data types they use, I believe that the only way that a receiving application can be sure that it is able to process all the structured data correctly, and know the correct way to present it, is where there is a tight implementation guide specifying exactly what data elements the CDA document can contain, how these are to be understood, and that there is strict and reliable conformance checking regime in place. The authoring application may know that the narrative is generated, or it may not. (the most common reason why it may not know is that the CDA document is being built by middle ware – a green CDA approach – and the narrative is an independent input of uncertain source).

So how can the rendering application know that the narrative doesn’t contain anything not in the structured data?

Entry.typeCode

The answer to this question is found in the typeCode on each entry. Here’s the relevant part of the CDA RMIM:

cda-section

A section has narrative (the “text”), one or more entries which have typeCode attributes, and nested sections. The possible values for typeCode are:

COMP (component) [default] The associated entry is a component of the section. No semantic relationship is implied.
DRIV (is derived from) The narrative was rendered from the CDA Entries, and contains no clinical content not derived from the entries.

So you can tell, from the entries in the section, whether the narrative was generated from the entries, and doesn’t contain any other data.

The typeCode=”DRIV” attribute is a little difficult to interpret. Here’s some notes about how to understand this:

  • If there’s no entries, and the section contains text, then the text is not generated from the entries (pretty obvious)
  • If there’s one or more entries, and any of them claim typeCode=”DRIV”, then the narrative is entirely generated from the entries
  • If some of the entries don’t have a type code, or the typeCode=”COMP”, then those entries aren’t represented in the narrative
  • There’s no way to indicate that an entry is represented in the narrative unless you claim that the entire narrative for the section is generated
  • The implication is that the entries relate to the section that contains them, though this is never explicitly stated

Here’s some example fragments:

<!-- a section with autogenerated text -->
 <section>
  <text>The patient has no problems</text>
  <entry typeCode="DRIV">
   <!-- an observation containing an assertion that there is no problems -->
  </entry>
 </section>

 <!-- a section with human modified narrative -->
 <section>
  <text>
    [a table of the patient's problems, built by application] 
    <!-- additional text added by a human: ->
    <p>The patient also has renal failure</p>
  </text> 
  <entry typeCode="DRIV">
   <!-- an observation with a problem -->
  </entry>
  <entry typeCode="DRIV">
   <!-- an observation with a problem -->
  </entry>
 </section>

 <!-- a section with autogenerated text -->
 <section>
  <text>The patient has no problems</text>
  <entry typeCode="DRIV">
   <!-- an observation containing an assertion that there is no problems -->
  </entry>
  <entry>
   <!-- an act with audit information that's not in the narrative -->
  </entry>
 </section>

So, in theory, you don’t need to render the narrative if all sections in the CDA document have no <text> element, or they have at least one entry with typeCode=”DRIV”. For this purpose, you can ignore sections that contain other sections and no text of their own (though if they have no text, and nested sections, it may be necessary to take note of their title).

Lloyd McKenzie has kindly contributed an XPath predicate for this test:

 ClinicalDocument[component/structuredBody[not(descendant::
    component/section[text and not(entry[@type='DRIV'])])]]

Practical Considerations

Note that I said that in theory this test works. However, in practice, there’s a number of problems associated with this:

  • The CDA rules about typeCode are not very prominent (see section 4.3.4.2), and are often overlooked
  • The CDA rules are not well documented, and even when documented more clearly, people find them very opaque
  • For a variety of reasons, the division between sections isn’t as clear as everyone would like, and entries sometimes end up in more than one section narrative, or in the wrong section for a different reason
  • Even amongst CDA implementation guide writers, the correct use and/or impact of typeCode=”DRIV” is easily overlooked.  In fact, the implementation guides I have contributed to have been wrong on this in the past (NEHTA guides).
  • Even if the CDA IG writers get it right, they may not anticipate the real world usage correctly, or they may provide an example which people copy without understanding (see the CCDA examples which include typeCode=”DRIV” on some examples and not others with no explanation)

What this means is that in practice, depending on typeCode=”DRIV” is unreliable due to poor compliance with the specification in this regard.

And in effect, then, you can only ignore the narrative and render the data if you’re really confident in the CDA implementation guide, and the conformance process associated with it’s implementation.

Explaining to the users what is going on

Ideally, an application would differentiate to the users between “displaying the data in the document” and “displaying the document”. Ideally, also, the users would actually understand the difference. But that’s not the world we live in. I think that this problem underlines the dangers of not rendering the narrative – you need to be really confident before you make that decision.

 

Question: Interpretation of multiple pre-conditions in CDA

Question:

Since a CDA R2 clinical statement can be associated with zero or more “Criterion” through precondition, what would happen in case there are TWO Criterions, one is TRUE and other is FALSE? What should be the default operator in order to arrive at the final decision point whether a service/activity should be performed or not? Should it be allTrue (AND) or atLeastOneTrue(OR)? or in the context of a specific IG, we can make our own statements as CDA is silent on this? I see that HQMF specification explicitly states AND, OR and other operators however this is not the case for CDA.

Background:

A clinical statement in CDA R2 can be associated with zero or more “Criterion” through “precondition”:

CDA defines precondition like this:

“The precondition class, derived from the ActRelationship class, is used along with the Criterion class to express a condition that must hold true before some over activity occurs.”

This means that the precondition must be true for a service/activity to be performed.

Answer:

There’s a property defined on the RIM ActRelationship (which is the base class underlying the CDA precondition) called conjunctionCode, which is defined as:

A code specifying the logical conjunction of the criteria among all the condition-links of Acts (e.g., “and”, “or”, or “exclusive-or”)

So if this attribute were present on the CDA precondition class that would answer your question. However, as you can see, the CDA precondition class has no such attribute as “conjunctionCode” – although it inherits it from the RI, the value has been fixed to the nullFlavor “No Information”,  and so it doesn’t need to be shown on the diagram. And therefore, technically, in CDA, you can’t evaluate the meaning of multiple -pre-conditions. But that’s being pedantic. In most cases, where a committee rules out the use of an attribute like that, there’s an implicit default value.

And it turns out that this is the case in CDA. When the CDA specification says, in text:

“The precondition class, derived from the ActRelationship class, is used along with the Criterion class to express a condition that must hold true before some over activity occurs.”

That answers the question: the condition must hold true. And so if you state more than one condition, then each of them is a condition that must hold true – so the implicit default conjunctionCode is “AND”.

So the direct answer to the question: they must all be true.

Extensibility

Further, by the rules of CDA extension, you can’t effectively change this. The rules say:

Extensions should not change the meaning of any of the standard data items, and receivers must be able to safely ignore these elements

You can’t add conjunctionCode as an extension without changing the meaning of the standardised data elements (or, more strictly, you could add it, but the only value you could use would be “and”). Adding it by a different name wouldn’t make any difference either.

But if you really wanted to do this, and the most you could do is add the or-constraints themselves as extensions – but then you have to somehow choose which preconditions a general CDA process would be aware of – which sounds quite unsatisfactory to me (and you can’t – legally, at least – simply say that you can ignore the general CDA processor case either).

This is the kind of case where it’s very difficult to extend CDA and get it to do what you want.

Question: Complex reference ranges for observations in CDA

Question:

I am wondering if it is possible to include a set of static reference data inside a CDA document, for example WHO percentile information for weight, as structured contents? I am not able to find a proper method to cater for this based on the CDA R2 R-MIMM diagram.

Answer:

I’m afraid that the answer is not very encouraging. This content would definitely go in the reference range section, which looks like this:

To include the data in structured form, we would have to either use the data structures provided, or extensions. First, notes about the existing fields on Observation Range:

  • classCode – the type of class that this is. Effectively fixed to an observation, and none of the possible values relate to this discussion
  • moodCode – a fixed value which is irrelevant for this discussion
  • code – the kind of observation that this is. The code is “what type of observation this is”, and it’s since the observation is a criterion on weight, that’s what the code should be.
  • text – a free text description of the reference range
  • value – a value that specifies the actual reference range
  • interpretationCode – a code that explains how the reference range should be interpreted. This can only be one of a limited set of codes

The following values are allowed for interpretation code:

  • B     better
  • D     decreased
  • U     increased
  • W     worse
  • <     low off scale
  • >     high off scale
  • A     Abnormal
  • AA     Abnormal alert
  • HH     High alert
  • LL     Low alert
  • H     High
  • HH     High alert
  • L     Low
  • LL     Low alert
  • N    Normal
  • I     intermediate
  • MS     moderately susceptible
  • R     resistent
  • S     susceptible
  • VS     very susceptible

Few of these codes – which primarily relate to the interpretation of a value, not a reference range are useful. I’d only count N, A, and AA as relevant. The percentile data is nearest to “Normal” but I don’t think that really covers something that has 1% and 99% values properly.

So, what are the options for including the percentile information as structured data?

  • It is possible to include this information in the text of the observation range, but that doesn’t count as “structured contents”, and it makes it pretty much impossible for a UI to do anything clever with it. Still, I’ve shown an example of this below.
  • We could include it as a series of reference ranges with a made up code that combines the age in months and the percentile, something like “5m-1%” as the code, and with the value a simple PQ (“5.2″ in this case). This is kind of technically misusing code – though it certainly wouldn’t be the only case by a long shot (in fact, the only way you can use code is effectively to misuse it). Still, it doesn’t work very well in this case: the codes would be an arbitrary invented set of codes, and who would know how to interpret them? The display is likely to be misleading or wrong from a system that doesn’t know them
  • we could add the information as a set of extensions using some XML of personal choice in a foreign namespace. This would work, though extensions are unwelcome for many CDA adopters. If you did this, you should really include the text for the sake of systems that don’t understand the extensions – which is why I show the example below, as a base for extending
  • It would also be possible to put the structured data straight in as the content of the text, with some media type to indicate what type it has. I guess it would be text/xml + any xml of your choice to represent the the content. But this is likely a bad idea since most systems would not interpret it correctly, and it’s not really consistent with the idea of a text representation of the reference range

None of those are good options, I’m afraid.

Note for v3 experts: The CDA model is just too stripped down here. The Lab RMIM defines criterion on the reference range, but you can’t introduce that as an extension to CDA (clearly qualifies the reference range), but it includes some problems of it’s own, and the correct interpretation codes are still missing.

Example with the percentiles as text:

<entry>
  <observation classCode="OBS" moodCode="EVN">
    <id root="$UUID"/>
    <!--SNOMED CT code for height -->
    <code code="$Code" codeSystem="$codeSystem" codeSystemName="$codeSystemName" displayName="Body Weight"/>
    <statusCode code="completed"/>
    <effectiveTime value="20120913"/>
    <!-- Weight -->
    <value unit="kg" value="5.30" xsi:type="PQ"/>
    <referenceRange>
      <observationRange>
        <text>
Weight-for-age GIRLS: Birth to 5 years (percentiles), weight in kg

Y: M  Month   1st   3rd   5th   15th  25th  50th  75th  85th  95th  97th  99th 
0: 0  0        2.3   2.4   2.5   2.8   2.9   3.2   3.6   3.7   4.0   4.2   4.4 
0: 1  1        3.0   3.2   3.3   3.6   3.8   4.2   4.6   4.8   5.2   5.4   5.7 
0: 2  2        3.8   4.0   4.1   4.5   4.7   5.1   5.6   5.9   6.3   6.5   6.9 
0: 3  3        4.4   4.6   4.7   5.1   5.4   5.8   6.4   6.7   7.2   7.4   7.8 
0: 4  4        4.8   5.1   5.2   5.6   5.9   6.4   7.0   7.3   7.9   8.1   8.6 
0: 5  5        5.2   5.5   5.6   6.1   6.4   6.9   7.5   7.8   8.4   8.7   9.2 
0: 6  6        5.5   5.8   6.0   6.4   6.7   7.3   7.9   8.3   8.9   9.2   9.7 
0: 7  7        5.8   6.1   6.3   6.7   7.0   7.6   8.3   8.7   9.4   9.6  10.2 
0: 8  8        6.0   6.3   6.5   7.0   7.3   7.9   8.6   9.0   9.7  10.0  10.6 
0: 9  9        6.2   6.6   6.8   7.3   7.6   8.2   8.9   9.3  10.1  10.4  11.0 
0:10  10       6.4   6.8   7.0   7.5   7.8   8.5   9.2   9.6  10.4  10.7  11.3 
0:11  11       6.6   7.0   7.2   7.7   8.0   8.7   9.5   9.9  10.7  11.0  11.7 
1: 0  12       6.8   7.1   7.3   7.9   8.2   8.9   9.7  10.2  11.0  11.3  12.0 
1: 1  13       6.9   7.3   7.5   8.1   8.4   9.2  10.0  10.4  11.3  11.6  12.3 
1: 2  14       7.1   7.5   7.7   8.3   8.6   9.4  10.2  10.7  11.5  11.9  12.6 
1: 3  15       7.3   7.7   7.9   8.5   8.8   9.6  10.4  10.9  11.8  12.2  12.9 
1: 4  16       7.4   7.8   8.1   8.7   9.0   9.8  10.7  11.2  12.1  12.5  13.2 
1: 5  17       7.6   8.0   8.2   8.8   9.2  10.0  10.9  11.4  12.3  12.7  13.5 
1: 6  18       7.8   8.2   8.4   9.0   9.4  10.2  11.1  11.6  12.6  13.0  13.8 
1: 7  19       7.9   8.3   8.6   9.2   9.6  10.4  11.4  11.9  12.9  13.3  14.1 
1: 8  20       8.1   8.5   8.7   9.4   9.8  10.6  11.6  12.1  13.1  13.5  14.4 
1: 9  21       8.2   8.7   8.9   9.6  10.0  10.9  11.8  12.4  13.4  13.8  14.6 
1:10  22       8.4   8.8   9.1   9.8  10.2  11.1  12.0  12.6  13.6  14.1  14.9 
1:11  23       8.5   9.0   9.2   9.9  10.4  11.3  12.3  12.8  13.9  14.3  15.2 
2: 0  24       8.7   9.2   9.4  10.1  10.6  11.5  12.5  13.1  14.2  14.6  15.5 
2: 1  25       8.9   9.3   9.6  10.3  10.8  11.7  12.7  13.3  14.4  14.9  15.8 
2: 2  26       9.0   9.5   9.8  10.5  10.9  11.9  12.9  13.6  14.7  15.2  16.1 
2: 3  27       9.2   9.6   9.9  10.7  11.1  12.1  13.2  13.8  15.0  15.4  16.4 
2: 4  28       9.3   9.8  10.1  10.8  11.3  12.3  13.4  14.0  15.2  15.7  16.7 
2: 5  29       9.5  10.0  10.2  11.0  11.5  12.5  13.6  14.3  15.5  16.0  17.0 
2: 6  30       9.6  10.1  10.4  11.2  11.7  12.7  13.8  14.5  15.7  16.2  17.3 
2: 7  31       9.7  10.3  10.5  11.3  11.9  12.9  14.1  14.7  16.0  16.5  17.6 
2: 8  32       9.9  10.4  10.7  11.5  12.0  13.1  14.3  15.0  16.2  16.8  17.8 
2: 9  33      10.0  10.5  10.8  11.7  12.2  13.3  14.5  15.2  16.5  17.0  18.1 
2:10  34      10.1  10.7  11.0  11.8  12.4  13.5  14.7  15.4  16.8  17.3  18.4 
2:11  35      10.3  10.8  11.1  12.0  12.5  13.7  14.9  15.7  17.0  17.6  18.7 
3: 0  36      10.4  11.0  11.3  12.1  12.7  13.9  15.1  15.9  17.3  17.8  19.0 
3: 1  37      10.5  11.1  11.4  12.3  12.9  14.0  15.3  16.1  17.5  18.1  19.3 
3: 2  38      10.6  11.2  11.6  12.5  13.0  14.2  15.6  16.3  17.8  18.4  19.6 
3: 3  39      10.8  11.4  11.7  12.6  13.2  14.4  15.8  16.6  18.0  18.6  19.9 
3: 4  40      10.9  11.5  11.8  12.8  13.4  14.6  16.0  16.8  18.3  18.9  20.2 
3: 5  41      11.0  11.6  12.0  12.9  13.5  14.8  16.2  17.0  18.6  19.2  20.5 
3: 6  42      11.1  11.8  12.1  13.1  13.7  15.0  16.4  17.3  18.8  19.5  20.8 
3: 7  43      11.3  11.9  12.2  13.2  13.9  15.2  16.6  17.5  19.1  19.7  21.1 
3: 8  44      11.4  12.0  12.4  13.4  14.0  15.3  16.8  17.7  19.3  20.0  21.4 
3: 9  45      11.5  12.1  12.5  13.5  14.2  15.5  17.0  17.9  19.6  20.3  21.7 
3:10  46      11.6  12.3  12.6  13.7  14.3  15.7  17.3  18.2  19.9  20.6  22.0 
3:11  47      11.7  12.4  12.8  13.8  14.5  15.9  17.5  18.4  20.1  20.8  22.3 
4: 0  48      11.8  12.5  12.9  14.0  14.7  16.1  17.7  18.6  20.4  21.1  22.6 
4: 1  49      11.9  12.6  13.0  14.1  14.8  16.3  17.9  18.9  20.6  21.4  22.9 
4: 2  50      12.1  12.8  13.2  14.3  15.0  16.4  18.1  19.1  20.9  21.7  23.2 
4: 3  51      12.2  12.9  13.3  14.4  15.1  16.6  18.3  19.3  21.2  22.0  23.5 
4: 4  52      12.3  13.0  13.4  14.5  15.3  16.8  18.5  19.5  21.4  22.2  23.9 
4: 5  53      12.4  13.1  13.5  14.7  15.4  17.0  18.7  19.8  21.7  22.5  24.2 
4: 6  54      12.5  13.2  13.7  14.8  15.6  17.2  18.9  20.0  22.0  22.8  24.5 
4: 7  55      12.6  13.4  13.8  15.0  15.8  17.3  19.1  20.2  22.2  23.1  24.8 
4: 8  56      12.7  13.5  13.9  15.1  15.9  17.5  19.3  20.4  22.5  23.3  25.1 
4: 9  57      12.8  13.6  14.0  15.3  16.1  17.7  19.6  20.7  22.7  23.6  25.4 
4:10  58      12.9  13.7  14.2  15.4  16.2  17.9  19.8  20.9  23.0  23.9  25.7 
4:11  59      13.1  13.8  14.3  15.5  16.4  18.0  20.0  21.1  23.3  24.2  26.0 
5: 0  60      13.2  14.0  14.4  15.7  16.5  18.2  20.2  21.3  23.5  24.4  26.3 
        </text>
      </observationRange>
    </referenceRange>
  </observation>
</entry>

Note: no claim about interpretationCode in this example.

Problems with PBS codes

The Australian PBS codes are used to describe the medications for which the Australian government offers rebates under the Australian Pharmaceutical Benefits Scheme. The codes are published as part of the rules definitions tables, and can be found here. In living memory, the codes have consisted of 4 digit codes followed by a alphabetical check letter. For instance, the code for a particular packaging of Simvastatin (used to be a clinical interest of mine) is “2011W”, and you can get the full details for this code at http://www.pbs.gov.au/medicine/item/2011W (note that this is not case sensitive, and http://www.pbs.gov.au/medicine/item/2011w also works).

However, the 4 digit codes are starting to run out. As I previously discussed, the PBS team won’t be reusing old codes for new meanings, so that means that they’re going to start using longer codes. That’s forecasted to happen in May/June this year.

That’s where the fun starts. From my post from before, the proper representation for a PBS code in a CDA document is this:

<code code=”1471K” codeSystem=”1.2.36.1.2001.1005.22″/>

and in a v2 message, it would be something like this:

 |1471K^^PBS|

(Though there’s no v2 registered code system for PBS – I’d like to think that means that no one is exchanging PBS codes in v2, but I think people are. We talked about defining PBS on an Australian basis, but we never formalised that)

The problem

However there’s a problem: when Medicare were updating their systems to handle longer PBS codes (something that was done last year after it was decided that PBS would use longer codes), they changed their interfaces so that the PBS code would be represented like this (quoted from one of several rules published as amendments to the National Health Act):

PBS/RPBS Item Code: Six bytes, right justified, zero filled, five bytes numeric followed by one byte alphabetic check character, being the code for the pharmaceutical benefit which appears in the Schedule of Pharmaceutical Benefits for Approved Pharmacists published by the Department of Health and Ageing. A zero code is to be used in the case of Repatriation items which are not included in the Schedule but have been prior approved by the Department of Veterans’ Affairs.

Valid values include: 0-9, A – Z. Alpha character must be in upper case.

So all the DHS/Medicare interfaces prefix the PBS code with a 0, like this: 01471K. Note that many of the medicare interfaces are fixed width file interfaces, and they don’t really have a lot of choice but to fix the width of the field, and it’s going to be either prefixed with spaces or zeroes.  Whatever,  the PBS Authority (DOHA) that publishes the codes doesn’t prefix with 0.

I discussed this with the team that defines the PBS codes. From their point of view, the code is an integer value, with an accompanying check character, and whether or not the integer value has any ’0′ digits prefixed to it is not significant, doesn’t affect the check digit calculation, and doesn’t affect the interpretation of the code. Whether 0 is prefixed in any given context is an implementation detail, and they are comfortable with people using either form.

Note that they don’t use prefixed codes – the ascii and xml formats that they distribute don’t include a leading 0, and http://www.pbs.gov.au/medicine/item/02011W is not found (based on the links above). That’d be the implementation aspect of it showing up.

As a side note, the PBS codes already include 2 and 3 digit codes for some special items. For instance, 183P is Chlorhexidine Acetate   (use as additive only). Note that no variation of http://www.pbs.gov.au/medicine/item/183P that I could think of trying worked, so I presume that these special codes are not published through the PBS web site. Btw, they PBS team also informed me that they reserve the right to use codes longer than 5 digits in the future, but I think we can safely assume that this won’t happen since DHS can’t support it without changing their interfaces again, which isn’t going to happen any time soon.

Because one of the first document types to be uploaded to the pcEHR was the medicare records, and because they use the 0-prefixed form internally, naturally, the medicare documents contained this form:

<code code=”01471K” codeSystem=”1.2.36.1.2001.1005.22″/

And because of the way this is implemented internally in the pcEHR, the pcEHR is therefore configured to only accept the 0-prefixed form. And now vendors that are testing to their documents with the pcEHR are running into this as a problem, since they do not prefix the existing codes with 0s.

Solution

There are various possible solutions:

  1. Get DHS/Medicare to change to not prefix the codes with 0
  2. Insist that PBS codes are always prefixed with 0 values to 5 digits when they are exchanged between systems (but not displayed to users?)
  3. Leave it that all systems should accept codes prefixed with 0s or not and know that the code itself is the same either way
  4. Say that the medicare interfaces and the pcEHR documents always (and must) prefix with 0, but in other contexts, it doesn’t matter (see rule #3)

I think we can safely say that the first isn’t a good idea. The second sounds like a good idea unless one day we start using 6 digit codes, but what happens to existing interfaces? #3 sounds like a good idea, but is really tough for systems, since the codes are suffixed with an alpha character – this violates normal rules for handling code systems, and special cases are always bad. The 4th is a variation of the 3rd that spreads the pain around differently (still involves special cases).

It doesn’t seem obvious to me what is the right solution here. This blog post is to solicit opinions from the various vendors etc that are affected by this problem. You can either comment on the post here, or if that is counter to your policy, you can comment to me directly. (Note that if you have a relationship with NEHTA, and it’s necessary, you can comment to me under NDA if you wish).

At some stage I’ll have to update the registration for the code system to clarify this, but I’ll wait for now. Also, I think it’s probably necessary for us to get a v2 code system for PBS codes. Let me know if you exchange PBS codes in v2 messages.

Update (13-Feb 2013): DHS advises that: “PBS Online will accept either a zero-filled or non zero-filled PBS item code. In practice some Software Vendors zero-fill and some do not.”

Question: How does the HL7 v3 Dynamic Model work

Question:

What is the actual usage of HL7 dynamic model? The interaction defines trigger event, composite message type, and receiver responsibilities. But does it really matter in message exchange? How are trigger events and receiver responsibilities expected to be used?

Answer:

Trigger events provide context about when interactions are expected to be exchanged.  For example, a particular notification interaction might be expected to be sent when the state of an object changes to “completed”.  In theory, a system that fails to initiate an interaction when the event described by the trigger event occurs could be considered non-conformant.  However, the trigger event alone isn’t sufficient for an interaction to flow.  The application also needs to have an appropriate recipient defined and meet other business rules such as access permissions.  As well, there are no rules about exactly how quickly after the occurrence of the event the interaction needs to actually be sent over the wire.  The best that can really be done is to confirm that a compliant system is *capable* of sending the interaction in response to the defined triggering condition.

Another consideration is that some interactions may share the same trigger event.  Frequently this is a acknowledgement interaction and a notification interaction, both triggered by a state transition caused by some sort of request interaction.  This means that if a system supports both interactions, it would be expected to invoke both kinds when the event occurs.

Finally, trigger events give a little bit of insight into expected application behaviour.  If the trigger event is a state transition, then that identifies that the application actually changes the state of the specified object type.

Receiver responsibilities are more important.  They define the “application acknowledgement” that is expected in response to a given interaction.  For example, an interaction that requests that a prescription be suspended might have receiver responsibilities to either change the state of the prescription (fire a trigger event) and send an acknowledgement interaction indicating the prescription has been changed or, alternatively, to send a “refusal” interaction indicating that the state of the prescription wasn’t changed and possibly indicating why.

Most commonly receiver responsibilities follow simple patterns.  Query interactions have a responsibility to send a response.  State transition requests and fulfilment requests typically have alternate receiver responsibilities for both acceptance and refusal.  Notifications generally have no receiver responsibilities.  They might receive back an accept acknowledgement, but no application-level acknowledgement is expected.

However, receiver responsibilities can get more sophisticated.  For example, a request to fulfil an order might be met with a counter-proposal interaction that says “I can’t do that, but I could do this instead”, which might in turn have a receiver responsibility to agree or refuse.  These patterns are rare, in part because they don’t work well in synchronous architectures.

HL7 has been looking for alternative dynamic model that provides broader capabilities, such as the ability to indicate that a lab system, after promising to fulfil an order also has a responsibility to later send the results, or perhaps a cancellation of their promise indicating they were unable to fulfil the test (lost the sample, etc.)  However, it’s been challenging to do this in a way that doesn’t make v3 more overwhelmingly complex than it already is, both for committees designing the specifications and the implementers who must realize them.

Note: Lloyd McKenzie provided this answer – thanks Lloyd

Question: v3 Lab Ordering Support

Question

I have read about some articles utilizing some HL7 v3 Laboratory interactions such as Order Fulfillment Request (POLB_IN211100) and order confirm response (POLB_IN221000). But in current version of HL7 standard Laboratory domain only result related interactions are listed. Where can I find definitions of these interactions and trigger events? I searched them on internet but find few information.

Answer

Well, the key point is this quote from the v3 lab topic (part of the v3 specification):

The scope of Laboratory Release 1 is restricted to the Result Topic

In the earlier versions of the Laboratory topic, up until Sept 2006, the laboratroy cycle included these interactions, and a general ordering pattern. However they were withdrawn in January 2007 ballot, with this comment:

LabSIG has withdrawn the domain ballot for Lab with the intent to ballot by topic. The reason for this is to recognize the work being done by the Orders/Observations committee to develop a common order (now defined as Composite Order) and the work being developed by LabSIG to take the existing balloted material to create result topic. By splitting out the lab domain ballot by topic provides the flexibility to support the work in development by the multiple domains working with Orders/Observations to develop a common order with each domain adopting an implementation constraint for the common order for their area.

The composite order was balloted in May 2009. The direct link to this (HL7 members only) is: http://www.hl7.org/v3ballotarchive_temp_4698CDC3-1C23-BA17-0C1027B887D05D69/v3ballot2009MAY/html/domains/uvor/uvor.htm. Note that a subsequent ballot was postponed – I don’t know where it stands now (though it is not normative).

Interpreting RIM Objects Safely (Exclusion Statements)

One of the most difficult parts of the RIM based content models – including CDA – is how to interpret and/or query them safely. In the past I volunteered to write something about this:

Grahame: As mentioned in Q1 MnM this morning, I volunteered to do this in the past. I agree that we need to progress this

I did spend some time on this, and it’s rather complicated (which meant I never finished it). In addition to being asked about the status of this earlier in the week, I was reminded of this forcefully when I saw Keith’s post about exclusion statements, which was in response to my earlier post about them.

Keith proposes to represent the assertion of a lack of allergies like this:

<observation classCode="OBS" moodCode="EVN" negationInd="true">
  <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
  <statusCode code="completed"/>
   <value xsi:type="CD" code="419199007"
     codeSystem="2.16.840.1.113883.6.96" displayName="Allergy to Substance"/>
 </observation>

I have stripped out the templateId, id, and effectiveTime attributes, since they are not relevant to the subject at hand. On the other hand, I left statusCode in, since it’s relevant. This compares with the NEHTA representation of the same concept:

<observation classCode="OBS" moodCode="EVN">
  <code code="103.16302.120.1.1" codeSystem="1.2.36.1.2001.1001.101" 
    displayName="Global Statement" />
  <value code="01" codeSystem="1.2.36.1.2001.1001.101.104.16299" 
    displayName="Nil Known" xsi:type="CD" />
 </observation>

These are rather different approaches. I don’t believe that either is wrong, though I admit that the representation Keith proposes is more aligned with general HL7 thinking. The NEHTA approach works differently because it is constrained to be an implementation of a solution to this problem based on an entirely independent definitional stack that starts from the definitions in openEHR. This has both strengths and weaknesses. The primary weakness is that it’s hard to be well aligned with the CCDA community. The strength is that you get a simpler solution. In this case, the exclusion statement, which was defined by the clinical consultation process is represented as “I observe (observation) that there are no allergies (global statement) because there are ‘nil known’ (value)” whereas Keith’s representation is “I observe (observation) that I can assert (Assertion) that the patient has allergies (value) NOT (negationInd)”.

The first note with regard to querying/interpreting RIM statements, then, is that you can say pretty much the same thing in completely different ways. This is a widely made observation, but worth repeating here. The RIM is essentially the grammar for a constrained language. Like all useful languages, there’s more than one way to say the same thing. You can say the same thing in several different ways. The same thing can be said in multiple forms. I don’t think that it’s possible to strip the RIM down to the point where there’s only one way to say things without removing the ability to say many things that need to be said. In particular, I don’t think that removing the flexibility to choose between structure and terminology – the source of much of this variation – is possible or useful.

The second note is that you can’t really start to understand RIM statements until you understand the terminologies. This is no different to saying that you can’t understand English until you know what the words mean. But implementation is still highly focused on the structures, and formal terminology support in applications lags far behind. Safe interpretation of RIM statements depends on solid and correct interpretation and usage of the terms. I won’t pursue this further in this post other than to say, this is a hard thing to get right.

In NEHTA, the clinicians are very insistent about the limited scope of the negation statement: they are never claiming that the patient does not have any allergies – only that none are known. This is pretty important to clinicians. But there’s a common language substitution that happens, between “We (patient and clinician) don’t know of any allergies” and “there are no allergies”. This same substitution happens in the RIM, and is evident in Keith’s example. I’m sure that Keith would be happy to confirm that there is no intent to claim that there is certain knowledge that the patient has no undiscovered allergies – but that’s actually what the RIM statement he offers claims. Generally, my view is that the RIM is weak on negation – there’s often a scope limitation on negation and the RIM doesn’t really capture that. (In the same way, the RIM is weak around uncertainty. Note though, that both these things are tremendously hard to describe).

But there’s some much simpler things that Keith’s example brings up

Be careful interpreting RIM Statements

Consider the difference in Keith’s xml between an assertion that a patient does have allergies, and that none are known:

 <observation classCode="OBS" moodCode="EVN">
  <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
  <value xsi:type="CD" code="419199007"
     codeSystem="2.16.840.1.113883.6.96" displayName="Allergy to Substance"/>
 </observation>

and

 <observation classCode="OBS" moodCode="EVN" negationInd="true">
  <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
  <value xsi:type="CD" code="419199007"
     codeSystem="2.16.840.1.113883.6.96" displayName="Allergy to Substance"/>
 </observation>

An important consequence of this is that whenever you look at an observation, you have to check whether it’s been negated. I’m sure that most implementers of CCDA aren’t checking whether the observations as described in CCDA are negated or not, since CCDA doesn’t say that they can be – but since they can be, you have to check. Painful, and I’m sure that this is going to prove to be the source of a number of adverse patient outcomes.

Note that the first example is that the patient is allergic to a substance, but with no substance. In practice, who’s ever going to say that, or try to read it without an error? So, at least in this context, the negationInd doesn’t stand alone. But consider a system that wanted to claim that the patient wasn’t allergic to tree nuts (my daughter is allergic to tree nuts, so this example comes naturally):

<observation classCode="OBS" moodCode="EVN" negationInd="true">
   <templateId root="2.16.840.1.113883.10.20.22.4.7"/>
   <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
   <value xsi:type="CD" code="282100009" codeSystem="2.16.840.1.113883.6.96"/>  
   <participant typeCode="CSM">
    <participantRole classCode="MANU">
     <playingEntity classCode="MMAT">
      <code code="FFY6MYO89N" displayName="TREE NUT"
        codeSystem="2.16.840.1.113883.4.9"/>
     </playingEntity>
    </participantRole>
   </participant>
</observation>

The only way to pick is to explicitly read the negationInd. So, you must always consult the negationInd. I doubt that many implementers mining data from (C)CDA documents know that this is possible, or remember to do it every time.

Aside: this is not allowed in NEHTA documents. If an element is not explicitly described in the implementation guide, it cannot be used if it’s not safe to ignore. Clearly, negationInd is not safe to ignore. CCDA seems to allow it anywhere – it never says it is not allowed, which makes rules like “MAY contain zero or one [0..1] @negationInd” rather confusing. (oh, if a CCDA template is closed, then it can’t have a @negationInd, but there was only one of those as of the last ballot).

Keiths’s second example is this:

<observation classCode="OBS" moodCode="EVN" nullFlavor="NI">
   <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
   <value xsi:type="CD" code="419199007"
     codeSystem="2.16.840.1.113883.6.96" displayName="Allergy to Substance"/>
 </observation>

This one is very subtle – there’s a nullFlavor on the observation, which means that all the content in the observation pertains to the thing that we don’t have any information about. It can be difficult to to interpret the scope of the NI – is the assertion that we have no information about? or the value? here, it’s probably not much difference. But not in other places. But the point is, you have to check the nullFlavor on the observation.

In a later post, I’ll get to the really complicated question (what my original intent was) – to what degree you have to walk up the heirarchy looking for @nullFlavor, @negationInd, and other things?

Unknown / Default Information

Keith’s example includes statusCode, with a status of completed. We eliminated this from the NEHTA representation because it’s just irrelevant to an implementer – what would it mean for the observation to be aborted? cancelled? suspended? Only one possible value makes sense, which is completed, so we eliminated this from the XML as just random noise that would confuse the implementers. But eliminating it leaves open the question, what is the value, and would it matter? It depends on context. But CDA itself eliminates uncertaintyCode, so you can never be sure whether an observation is uncertain or not – unless you are aware of any applicable guidance from the implementation guide.

So a final point (for this post) about interpreting RIM statements: it can only safely be done in partnership with good knowledge of the rules that were applied where the instance was generated. If you don’t know what these are, you won’t know how to intepret the content.