FHIR Messaging: Clinical data from ADT Messages
March 23, 2015 6 Comments
In the last post, we started to think about how we might be able to capture clinically useful information (naturally as FHIR resources) from the HL7 v2 messages that are used within many hospitals. These messages are used to move information between the various components of a hospital such as the PAS (Patient Administration System), the RIS (Radiology Information System), the LIS (Laboratory Information System) and so forth.
A common pattern is that the ‘downstream’ systems (LIS, RIS) ‘subscribe’ to these messages emitted from the PAS – generally via an Integration Engine. So all we’ll do is add ourselves to the list of interested parties, and we’ll get a copy of each message as well.
And actually, there’s no real reason why we should confine our attentions to just one hospital – if there are multiple co-operating hospitals then there’s no reason why we can’t receive messages from all of them, though we do need to be careful with identifiers (patient and visit) as these might overlap between providers.
Here’s what it might look like, with the arrows representing the flow of v2 messages:
The resources we identified that we could get from this data were:
- Encounters (always useful to know when people have been – or are – in hospital)
- Conditions – actually ‘Diagnoses’ relating to those encounters. Not as good as a formal problem list, but still helpful. (And, once we get the basics working, there are other Condition-related messages that we might tap into)
- Allergies – good to know those! Of course, the provenance of allergies from ADT messages might not be as good as coming from clinical messages, but still useful.
- Procedures – well, you would expect a hospital to know what procedures they are performing.
- Observations. Not quite sure what these represent in the context of an ADT message, but let’s store them.
Now we’re said that we’re in a messaging paradigm now, but what does that actually mean?
Well, as the diagram above shows. we are receiving messages that have been sent by another system (generally the PAS) in response to real world events that occur as the patient is treated in the hospital. This has a couple of implications for us:
- We need a way to link all the messages to the same patient (actually, the same encounter or visit for that patient). In fact, this extends to the actual model itself – there needs to be some link from each resource to the message that created it (or at least to the Encounter resource that represents it).
- We need to think about what the impact each message type has on our data model, which will vary according to the event that it is describing.
(Actually, in real-life we also need to consider how to manage ‘out of order’ messages – what do we do when, say, a bed transfer message arrives after a discharge message? This is an implementation issue, and so we’re going to ignore it in this discussion, but if you’re doing this for real then you will need to decide what strategy you are going to follow – for example you could reject such messages and make the sender decide what to do, or you could make a decision based on the date of the message – or anything else, an exercise for the reader 🙂 )
So what does our model look like? Well, here’s a first draft:
Note that all the ‘clinical’ resources have a link back to the encounter where we got them from. We’re going to need this to be able to properly manage changes that might occur due to workflow issues, and also as a kind of ‘provenance’ – knowing the source of clinical information is always important. (Actually, there’s a formal Provenance resource that we could use if we wanted to).
In most of the resources there is a property that makes sense:
- Condition: Encounter when condition first asserted. We’ll be setting the Condition.category to ‘diagnosis’, and creating a new Condition for each visit (as the diagnosis is about that visit).
- Observation: The healthcare event where this observation is made
- Procedure: The encounter when the procedure was performed
- AllergyIntolerance: This needs to be an extension, as there is no encounter reference available. You could argue that this should be similar to Condition – i.e. that there should ‘encounter first asserted’ property – if you feel strongly about it, then raise a change request on the spec – we’ll just use an extension.
We haven’t indicated any Patient, which all of the resources will have a reference to. As we’re getting information from different sources which will have their own patient referencing systems, this is going to need some thought which we’ll go into in the next post.
OK, so we have the model – let’s think about what each message is going to do to our model (we’ll come back to the question of linking all messages to the same ‘encounter stream’ later on).
There are a lot of messages to consider – over 60 of them in the 2.4 spec (which is the version we’re using)! The actual messages that are used will be different in each implementation, and we don’t have space to consider them all, so we’ll pick a selection that will highlight the major factors that we are going to need to consider.
The table below lists some of these messages and the impact on our model. For readability, we’ve just included the event code, so A01 is actually ADT^A01.
Some of these messages are reasonably straight forward to process – and others are not!.
(btw – it’s a good idea to read this in conjunction with the v2.4 spec – it’s in chapter 3 – Patient Administration)
|Message||Purpose||Has clinical data||Description|
|A01||Admit/Visit Notification||yes||This is the message that is sent when a patient is admitted to the hospital – ie assigned to a bed. When received by the system it will create a new Encounter resource, along with any clinical data.|
|A02||Transfer a Patient||No||This occurs when a patient changes physical location – for example they are moved to a different ward. It will update the encounter resource, but not the clinical data.
Interestingly, the spec notes that if there is any clinical data that changes, then the A08 message should be sent.
|A03||Discharge/End Visit||some||Sent when the patient is discharged. It will update the Encounter, and may include diagnosis & procedure information (but not allergy). Again, the recommendation is to use A08 if needed to update associated data|
|A04||Register Patient||yes||Similar to the A01, this indicates that the patient is at the hospital (eg in the EC – Emergency – department) rather than being formally admitted. We would create a new Encounter if we get this message.|
|A05||Pre-Admit a Patient||Yes||Will also create a new Encounter (and clinical data). Used during a pre-admission when the patient is examined before the actual date that they are admitted (this is common for planned – or ‘elective’ procedures).|
|A06||Change an Outpatient to an Inpatient||yes||Used when a patient who is being seen as an outpatient is admitted. For example someone is in a cardiology outpatient, and their condition is so serious that they are immediately admitted.
Interestingly, the spec stats that this may either change the existing encounter (the outpatient one) or create a new one. From our perspective, this raises the question of how to identify an encounter – which we will talk about soon. It also shows how the v2 spec is used in different ways for different implementations – you need to check these things!
|A07||Change an Inpatient to an Outpatient||yes||I’m not quite sure when this would be used – a discharge would seem more logical, but I guess there are systems that do it this way. Like the A06, this might either change an existing encounter or create a new one.|
|A08||Update Patient Information||yes||This changes the information associated with the patient, but which doesn’t effect the encounter. For example a new allergy is discovered. The encounter resource will be unaffected, but the clinical resources may well be.|
|A11||Cancel Admit / Cancel Visit Notification||no||This cancels an admission (A01) or a registration (A04) – either because of error, or because a decision is changed.
We’ll set the status of the encounter accordingly – but an interesting question is what to do with the clinical data. Should we invalidate that as well?
|A12||Cancel Transfer||no||When an A02 (transfer) is cancelled. The encounter status will be updated, but the clinical data we’ve extracted can remain.|
|A13||Cancel Discharge / Cancel End Visit||yes||Similar to the A12 event. The encounter only is updated|
|A28||Add Person or Patient Information||yes||This is similar to the A08, but is specifically intended to update patient related data between systems that may have different master patient databases. Like that event, the clinical data but not the encounter will be updated.|
|A31||Update Person Information||yes||This is specifically intended to update patient information in an Enterprise Master Patient Index ( EMPI). We can use these messages to update our patient store so we know who the patient identifiers refer to, though we could also extract the desired information from the PID segment of any of the messages.|
|A37||Unlink Patient Information||no||This is an ‘unlink’ message – see A40 for discussion. (There used to be an A36 – patient merge message that the A40 replaces)|
|A38||Cancel Pre-admit||no||Cancels an A05. The same considerations as for A11|
|A40||Merge Patient – Patient Identifier List||no||This event indicates that 2 patient identifiers are actually the same person, and we need to ‘merge’ them together. Patient identity is a really complicated topic that we’ve dodged so far, but this time we can’t. Having said that – how we manage it is going to be entirely dependent on how we implement patient identity.
Either way it needs careful thought, and the v2 spec has quite a lot to say about it.
|A45||Move Visit Information||no||Another potentially tricky one. The spec states that this is a ‘move at the visit identifier level’ – where the ‘patient account’ associated with the visit has changed. In other words – the patient has changed for this visit.
For us, it probably just means that the Encounter.subject property needs to be switched to the correct patient, but anything that involves patient identity has ramifications that we’ll need to think through.
We also need to think about the effect on the clinical data we’re extracting: do we change the subject there as well? Or – do we assume/mandate that an A08 message will come though and change it? If we do, then we’re assuming that an A08 is able to change the identifier (which is not mentioned in the spec), so it may be safest to change the subject property there as well.
|A50||Change Visit Number||This actually changes the visit number – the identifier that indicates that a sequence of messages is about the same visit. The actual effect for us will simply be to update the encounter|
So there’s a few things we’re going to need to give careful thought to – and these are likely to be ‘implementation dependant’ – i.e. they will vary between different hospitals.
- How is a single ‘visit’ identified – i.e. when we receive a message, how can we identify the Encounter that this message is going to effect?
- Which messages will create a new encounter, and which ones will update an existing one?
- How is the patient associated with the visit? There’s both the concept of the ‘patient account’ and the ‘patient identifier’ to think about.
- What do we do with the extracted clinical data when the patient identifier changes on the encounter? (A45)
- What is our strategy for updating clinical data within a visit when there are updates, as quite a few of the messages have the capability to include clinical data?
- Do we assume that each message has the complete set of data (i.e. a snapshot at a point in time), or are they updates? Suppose the A01 message has a particular diagnosis and an A02 doesn’t – do we remove it from the list of diagnoses for that visit?
- Should we ‘accumulate’ data? E.g. if the A02 contains a diagnosis that wasn’t in the A01 do we just append it to the list
- Do we update at all? Should we create a set of clinical data from the A01, and then assume/require that the source system send an A08 or A28 when the clinical data changes?
So there are a few things to think about (and I’m sure many others that we haven’t listed here). To keep things moving we’ll make the following assumptions (your assumptions may be different):
- That the PV1-19 contains the visit identifier – that’s the formal way that we’ll recognise messages belonging to the same visit. We’ll also use a combination of the MSH-4 (sending facility) and MSH-3 (sending application) to recognise messages from different hospitals (and different applications from within those hospitals)
- That the PID-3 holds all the patient identifiers.
- That we’ll use the A01, A04 or A05 to create the clinical data, and require an A08 or A28 for updates to that data – and that the update will be a snapshot – it will replace the data that is already present for that visit.
- That we’ll support multiple identifiers for a patient – but keep data against the identifier that was specified when we receive it. We’ll ‘resolve’ multiple identifiers for a patient at read-time. (ie when a querying system asks for data using an identifier, we’ll resolve all the identifiers for that person – using an EMPI – and return all the matching data).
- If the patient changes as a result of an A45, we’ll assume that there will be an A08 message that will follow with updated clinical information. This message will be able to change the subject associated with these resources.
- We’ll be tolerant of ‘out of order’ messages, but we’ll use the message time (MSH-7) to decide what action they will have. For example, if we’ve received a discharge message (A03) and so set the encounter.status to ‘finished’ and then we receive a bed transfer (A02) then the action will depend on the relative dates. If the transfer message is dated after the discharge then we’ll revert the status to ‘in-progress’, otherwise we’ll ignore the bed transfer (though we might update the status history).
In practice of course, we’d need to ensure that the source systems abide by these rules. If they don’t, then we’ll either need to change our system – or possibly use an Integration Engine to update messages from specific hospitals to make them compliant. For example, if a particular hospital does not emit A08, but does include a snapshot of clinical data in all update messages (like an A02) then the Integration Engine could create an additional A08.
So let’s take another look at our list of supported messages and what they are going do in our system.
|Message||Purpose||Action in our system|
|A01||Admit/Visit Notification||Create a new encounter and all related clinical data|
|A02||Transfer a Patient||Update the Encounter properties|
|A03||Discharge/End Visit||Update the Encounter properties|
|A04||Register Patient||Create a new encounter and all related clinical data|
|A05||Pre-Admit a Patient||Create a new encounter and all related clinical data|
|A06||Change an Outpatient to an Inpatient||Update the Encounter properties|
|A07||Change an Inpatient to an Outpatient||Update the Encounter properties|
|A08||Update Patient Information||Update the clinical data associated with this encounter. Note that this can change the subject associated with those resources as well.|
|A11||Cancel Admit / Cancel Visit Notification||Update the Encounter properties|
|A12||Cancel Transfer||Update the Encounter properties|
|A13||Cancel Discharge / Cancel End Visit||Update the Encounter properties|
|A28||Add Person or Patient Information||Update the clinical data associated with this encounter|
|A31||Update Person Information||Not quite sure about this one yet. It’s not really related to a particular encounter, so we’ll think about it a bit more when we start to consume other messages. For now, we’ll ignore it.|
|A37||Unlink Patient Information||Our patient identity system will no longer assume that these two identifiers refer to the same person.|
|A38||Cancel Pre-admit||Update the Encounter properties|
|A40||Merge Patient – Patient Identifier List||Mark the two identifiers as being the same person. (The message contains the ‘surviving’ identifier). We won’t actually merge them – we’ll just link them together. That way we can ‘unmerge’ – A37 – them later.|
|A45||Move Visit Information||Change the encounter.subject to reference the new patient identity|
|A50||Change Visit Number||Change the encounter.identifier property. Of course, an encounter may have multiple identifiers so we’ll need to make sure to change the correct one!|
That’s quite a chunk to think about! And hopefully explains why workflow is so important, and why this stuff can be so complicated! We’ve just looked at a small selection of messages – and all we’re really doing is to maintain a store of resources – imagine if we were writing a PAS ourselves!
In the next post we’ll delve into architectural issues and think about what we need in our FHIR server to support these messages. Oh, and if you disagree with anything in the post – feel free to make a comment!