Handling Test Data in Live

I can imagine that there are some of you who saw the headline and came here to chew me out because mixing LIVE and TEST data is a mortal sin and should never be done!

I agree that it shouldn’t be done, but, at least in healthcare, it happens and as an integration analyst or programmer you should be prepared to handle it.

Here’s the situation I found myself in.

A client is upgrading their Meditech Magic HIS from 5.66 to 5.67.

Their middleware is MDI Solutions MD-Link.  MD-Link uses Jython as its scripting language.

That means we are working with both a 5.66 and a 5.67 Test Ring.

If there’s interest I can talk about the challenges of running a dual test environment in another article,  but today I want to talk about my solution for testing a Point of Care interface where the vendor did not have a test environment.

The POC devices are glucometers that allow the user to pull in patient information from the vendor’s ADT server.

The vendor does not have a test ADT server.  When my client implemented, they purged test data before going live.

After verifying with vendor support that they could purge specific patients I opted to prepend facility mnemonics (initials) to both the MRN from PID.3 and the visit number from PID.18 for a specific list of test patients.

The tricky part with this comes when dealing with results for these patients as all that occurs in LIVE!

I say tricky because the MRN and Visit numbers in our test system will be used in our Live system as well.

While Meditech NMI (Non-Meditech Interfaces) Module will fail messages where different identifiers don’t match the Master Patient Index we still don’t want the wrong messages going to LIVE just in case the wrong message passes validation and ends up as part of someone’s record.

Fortunately our Lab Analyst only asked for four test patients, one for each of our facilities.

Here’s the code for the various steps.  Explanations are included in comments.

ADT from TEST to Vendor LIVE


# test patient mrns (PID.3)
allowed = ["G0000043","F0000006","M0000003","D0000002"]

# test patient visit numbers (PID.18)
visits = ["IN000026/17","IF0000004/17","IM0000002/17","ID0000001/17"]

# text to prepend to identifiers
prepend = ["GGH","GM","LM","PD"]
#this creates the message object in the custom task
hl7msg = util.makeHL7MessageFromXMLDocument(engine.inputDom)

mrn = hl7msg.getField('PID',3)
vn = hl7msg.getField('PID',18)
# check to see if both MRN and Visit are in the filter lists
if mrn in allowed and vn in visits:
        idx = allowed.index(mrn)
# the position of facility initials in the prepend array
# match that of the facility test pt in allowed

	hl7msg.setField(prepend[idx] + mrn,"PID",3)  # changes the value of the mrn
	hl7msg.setField(prepend[idx] + vn,"PID",18)  # changes the value of the visit
else:
	engine.cancel()   #prevents the message from passing to the next component

# this creates the outbound object and sends it to the next component.
engine.outputDom = util.makeXMLDocumentFromHL7Message('TaskData', hl7msg)<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

Vendor Results to Meditech TEST
The first 3 lines are the same so I left them out

# mrns have a set length of 8 chars,  visit numbers 12
mrn = hl7msg.getField('PID',3)[-8:]
vn = hl7msg.getField('PID',18)[-12:]
print vn
if mrn in allowed:
	idx = allowed.index(mrn)

        # as there is a chance that an MRN in Live might match a visit number in 
        # the list I want to check that the Test Patient MRN matches that 
        # patients visit no.

	if vn == visits[idx]:
				
		hl7msg.setField(mrn,"PID",3)
		hl7msg.setField(vn,"PID",18)
	else:
		engine.cancel()
else:
	engine.cancel()


engine.outputDom = util.makeXMLDocumentFromHL7Message('TaskData', hl7msg)

Advertisements

Leave a comment

Filed under MD-Link, Mixed Environments, Tips and Tricks

Why Healthcare Experience is Important for Healthcare IT Types

When I was a military medic I was fond of saying, “For every 100 people in healthcare there are 50 ways of doing things…49 of them are right.”

Any experienced HL7 programmer will tell you that sometimes much of the HL7 specification can be open to interpretation.

Take for instance the term MRN.   How many of you immediately recognize it to mean “Medical Record Number”?   What does that mean in your facility?  How does your HIS associate this to the account number (visit number,encounter number, etc)?

Many people will give different answers,  and they will mostly be correct.

That’s lesson #1:   Everyone does things differently.  If you have 10 years experience working for a single healthcare organization is can be something of a shock when you step out into the wider world.

That was a hard lesson for me to get my head around when I worked for an ED Systems vendor.  My only exposure to the healthcare world was in the military.  So while I could discuss how to implement something like The Ottawa Rules for deciding when an x-ray was necessary for an ankle fracture with front line clinical users,  I was often confused when discussing identifying patients through the registration process.

A few years ago, when the Ontario government was ramping up e-health,  HL7 became a very desirable thing to have on one’s resume.   Of course, its an essential skill for an HL7 Integrator.   However, many gloss over the requirement to understand exactly how healthcare works.

Case in point….I recently implemented a new interface with an outside organization.

When I looked at their spec I was baffled.

They wanted me to add an OBX segment to ADT-A01 messages.   In this segment,  they wanted me to add the hospital contact information.   At the same time, they gave me a code table so I could put the proper hospital code in the sending facility field in the MSH segment.

To those who don’t have a background in HL7 let me explain…an OBX segment usually contains the results of tests.  It is found in ORU messages (Observation Results).   ADT (Admission, Discharge and Transfer) don’t typically (as in ever) have OBX segments, because there are no results to report on.

So, this is like ordering a burger at a restaurant….. when it arrives,  you see that in place of a hamburger (or chicken, or tofu..) patty, there is a big slice of cherry pie….yes, its still edible,  but its just wrong on so many levels!

For the record,  I flat out refused to do this,  pointing out that the hospital information should be pulled from their database, at their presentation/application level.

I next looked at the messages they wanted…  Admit,  Cancel Admit, and Discharge messages.    They would match on the HCN as their Primary Key.

I sent them an email and pointed out that as they were looking for ER visits,  that quite often we do not have the patient’s HCN when they arrive,  and that sometimes we don’t even know who the patient is.    I followed up by pointing out that without accepting update messages,  that they would be getting bad data.

They responded by telling me that if I didn’t send an HCN in the A01  they would match on name, gender and date of birth.

There are at least three patients in our MPI who match on those three fields….

They seemed baffled by the fact that that could happen.

I also pointed out two things that should be obvious to anyone with enough experience in this field:

1)  A person could be identified in my system as Smith, Jim A,   and in their system he could be Smith, James Arnold,  and

2)  Sometimes Registration Clerks make mistakes….

They pointed out that previous sites they implemented with would store their messages and forward them once a day.   Translation “We built a bad interface, and made hospitals do extra work to make up for that.”.    I refused to do that,  because of the amount of work involved,  and the additional failure points.

They decided to proceed anyway…telling me that they still didn’t want Update messages because they wanted to avoid a “chatty interface”,  and “it would mean more work”.

The lack of experience,  and inability to take responsibility and fix a bad design means that this entity will be receiving bad data.    Its not a patient safety issue,  or I would’ve refused to implement it.

The fact that other organizations worked around The Bad means that those that designed this system (they have HL7 in their job titles)  will go on to other projects.   The Bad will continue….and eventually they might end up designing a mission critical system where a failure could result in a poor patient outcome.

Leave a comment

Filed under General Information

Sticking to standards for fun and profit (or just saving money…on integration programming…and Aspirin)

I remember when leaving the military in 2000,  hearing about all these high paying tech sector jobs in IT and how almost every community college had a special program that would take your average person who was baffled by their microwave and turn them into tech savvy IT gurus.

The end result was IT certificates and diplomas became next to worthless, because all it meant was that you could spout off IT terminology and theory like you knew what you’re talking about,  but have no idea how to actually apply this knowledge in a real world environment.  (I suspect many of these people become consultants to tv and movie writers )

Sadly, there is evidence that this is becoming more and more pervasive in the healthcare IT world.  While I’m addressing integration, I suspect its likely in all aspects.

In our public healthcare system we are often called upon to implement interfaces for a variety of purposes in various jurisdictional realms.

I once ran across a job posting for a “Senior Healthcare Integration Architect” for one of these jurisdictional bodies.   I was horrified (quite literally) to see that required knowledge list was extensive and contained a lot of things that I, an experienced and seasoned integration programmer,  had to look up,    but  listed as “desirable” (as in, its okay if you don’ t have this) were  HL7 integration, and  experience in a healthcare environment!

Those were optional….for a position with  “Healthcare” and “Integration” in the job title!

Therein lies the problem.   The belief that all systems are the same,  whether its transport logistics,   travel booking,   education,   healthcare…a system is a system is a system….right?   Yeah…NO!

So what happens is that someone with no idea on how hospitals or other healthcare entities operate gets into a position like that,  is told to design an interface,   does so,  using what he or she views as the “latest and greatest” of technologies.    This gets foisted on the reporting facilities who don’t have the expertise on staff (because you know….HL7 is the healthcare standard),   who then have to go out and find a vendor to design and maintain the interface.   Of course vendors don’t do things for free…so healthcare dollars that could have gone to patient care,  ends up going to some programmer,  working for some vendor.    All because some one didn’t think it was important to hire an Integration Architect who had any real experience in healthcare.

If everyone across these jurisdictional silos adopted a single standard (*cough*HL7*cough*),   either hired people with the understanding of how healthcare works,  or even consulted the workers in the trenches  the cost savings would be tangible.     Implementation time for projects would be reduced,  as would ongoing support costs and headaches.

The practice of hiring senior people from outside the industry,   who impose technical requirements that are so far out of the realm of the everyday hospital IT department is doing a disservice to those who provide healthcare services.

A case in point is the SPIRE project which was implemented in LHIN (Local Health Integration Network) 1 and 2 in Ontario.    This was a project that was set up to provide local doctor’s offices with lab and other reports on their patients electronically, in real time.

After several attempts to come up with an HL7 specification by other parties,   a meeting was held,  inviting the integration programmers for the hospitals involved.   We spent an afternoon cloistered in a conference room where we went through the HL7 standard specification,  field by field and hammered out a specification that could be met by any of the different Hospital Information Systems involved.

The result was an elegant, easy to implement solution.   No hiring outside vendors,  or struggling with Web Service connections.   A simple HL7 interface over MLLP through a tunnel,  or SFTP.

This should be the standard practice when implementing a new project.

HL7 isn’t new,  it isn’t cool,  it isn’t sexy…..but it works.   HL7 interfaces typically have very low overhead,  aren’t resource intensive and are extremely robust  (and almost every hospital has their own HL7 engine).

So why isn’t it universally implemented in healthcare?

 

2 Comments

Filed under General Integration

Adding an escaped linebreak in Javascript

If your working in Javascripting what happens when you try to add a line break like \br\ to your code?

Most parsers will choke on it as the backslash is also used as a string terminator.

Here’s how I did it:

var linebreak = String.fromCharCode(92) + ‘.br’ + String.fromCharCode(92);
if (seg[‘OBX.5’][‘OBX.5.1’].toString() == ”) {

    seg[‘OBX.5’][‘OBX.5.1’] = linebreak;

}

Fairly clean solution.

 

Leave a comment

Filed under Mirth Integration Engine, Tips and Tricks

Entity Relationships in Healthcare

While you’d think this would be quite simple,  from an programmer’s standpoint, one of the most confusing things in modern healthcare is how a patient’s record is managed.

The patient comes in,  is seen,  documented on and said documents are then associated to the patient.    Fairly simply right?   Once you start factoring in statistics and billing it gets much more complicated.   There are different types of visits that all must be handled different ways, and different facilities all have different ways of doing things.

What does this mean to the HL7 programmer?   If you’ve read this article, you’ll recall I talked about the differences in how facilities manage patient flow, and even the terminology they use can be different.   This can be daunting to a programmer with little experience in healthcare and can cause problems if systems lack the right combination of HL7 compliance and  flexibility to accommodate the myriad of configurations found in the real world.

One of the most frequent issues I’ve seen with healthcare systems,  especially integration is when they were designed by someone who does not understand how many different ways things can be done in the healthcare world.

Every integration programmer needs to understand the basic concept on how different parts of the electronic record fit together.   Most information systems organize data using a relational database model.   By that I mean that information is stored in different tables and not giant flat files.   It is essential that anyone designing systems for use in healthcare settings understand the differences between a patient and a patient visit,   between an order and a result,  and how different parts of the record tie together.   In order to gain this understanding one must have a firm grasp on how the relationship types 1 to 1 and 1 to many work and where they apply within a patient’s record.

An example of a 1 to 1 relationship might be patient visit to demographics,  but only if the patient doesn’t move residences during their stay.   One might say that patient visit to insurance might be a 1 to 1 but even in Canada where we have a single payor system a patient can have several insurance plans associated with a single visit.

While it should be obvious that a patient to patient visit relationship would be one to many as a single patient can present many times,  a not so obvious example is an order for tests would have a one to many relationship with the results.     The reason for this is that the order could comprise not only of different elements that need to be reported separately but there could be preliminary results,  final results and amended results.

Now that we have an idea on how some of these different tables tie together, now we talk how to identify different entities within the patient record.   The easiest way to break this down is to present the information in a table.

Properly formed HL7 messages will always contain a PID segment identifying the patient (unless of course there is no patient associated with the data).     This allows the receiving system to properly associate the incoming data with the correct patient and visit.

There are three types of matching when it comes to matching data to a particular record.   Person Match, Account Match and Accession Match.

A person is identified by an individual unique record within the Master Patient Index.    A Person Match will associate the data with the medical record belonging to a particular patient without matching it to a particular visit.   If you are updating a patient’s address or some other piece of demographic data this would be done using a Person Match.

An Account Match,  a.k.a  Visit Match,  or Encounter Match will match the data to a particular patient visit to your facility.  If a patient is admitted to your facility,  the patient’s location, admission and discharge times would all be associated to the account.   In a properly normalized database structure,  the system will know which person the visit is associated to automatically.

An Accession Match is where results are tied to a particular order.   If a physician orders a battery of tests called an “Order Set”  the typical Lab Information System will assign all the components of the order set to a particular unique accession or order number.    This number will be associated to a visit number,  which is then associated to a person.  All results sent via HL7 will be linked to this order by way of accession number.

All that is pretty straight forward but there are some pitfalls that may be encountered.  Take insurance information.   On the face of it you may consider insurance information be associated with the person,  however,  when you consider further you’d know that insurance also must be associated to the visit.   Insurance can change,  there fore if you have a change in insurance between visits you’d want insurance company A associated with visit A and company B with visit B.    Some system will also associate new insurance levels to the person,    to be recalled automatically when the person returns.

As you can see healthcare records maintenance is a complex thing.    Healthcare systems designers and programmers simply must have a solid understanding of how information fits together and be able to design their systems with the flexibility and scalability to accommodate the myriad of different methods existing today.

1 Comment

Filed under General Integration

Queue Alerts in Mirth

I have several channels where its important to know if queues are starting to build so that I can notify users and call the administrator of the receiving system.

In this particular case the email will go to myself,  2 colleagues in the IT department and the PACS Administrator as she needs to notify users.

The complicating factor here, and why I don’t use Mirth’s native alerting system is that we occasionally get more than 100 messages in queue.  I don’t want to generate an alert email for every message so I have some code that will only send out an alert every 3 hours.

To do this,  I have this code in my channel’s deploy script

var newtime = new Date(“January 1 1990”);        //This resets the ‘last alert’ variable ensuring the alert will fire if the alarm condition occurs shortly after deploying

globalMap.put(channelId + ‘_lastalert’,newtime);

In the post-processor script I put this code:

/Check to see if the alert has already been sent within the given timeframe
var lastsent = globalMap.get(channelId + ‘_lastalert’);            //last time an alert was sent
var rightnow = new Date();                                        //Current Datetime
var hLimit = 3;                                                    //Number of hours before resending an alert

var datediff = (rightnow – lastsent) / (1000 * 60 * 60);        //calculates hours since last alert went out

if (datediff < hLimit) {

return;

} else {

//This is the bit that does the heavy lifting

var controller = Packages.com.mirth.connect.server.controllers.ChannelStatisticsController.getInstance();
var channelName = Packages.com.mirth.connect.server.controllers.ChannelController.getInstance().getDeployedChannelById(channelId).getName();
var stats = controller.getStatistics(channelId);

//I include all these so you can see what’s available
var mSent = stats.getSent();
var mReceived = stats.getReceived();
var mQueued = stats.getQueued();
var mError = stats.getError();

var qAlarm = 20;                                                  //This is the limit of queued messages prior to triggering the alert

if (mQueued >= qAlarm) {

var emTo = ‘somone@email.com’;                                      //This is the email address you want to send the alert to.  Multiple emails can be separated with a comma
var emCC = ”;                                                      //This is any cc emails you want to include
var emFrom = ”;                                                  //This is the “From” address that will appear on your email
var emSubj = ‘Queue Alert for Channel: ‘ + channelName;              //This is the subject of the email
var emBody = ‘Queued messages detected on channel ‘ + channelName + ‘.\n’;
emBody = emBody + ‘Current number of queued messages is ‘ + mQueued.toString() + ‘.\n’;
emBody = emBody + ‘This alert will not be resent for ‘ + hLimit.toString() + ‘ hours.\nPlease do not respond to this email. This address is not monitored.’;

var smtpConn = SMTPConnectionFactory.createSMTPConnection();    //Opens email connection using email settings in your Mirth Connect Settings.

smtpConn.send(emTo,emCC,emFrom,emSubj,emBody);

globalMap.put(channelId + ‘_lastalert’,rightnow);

}
}

The code is portable, meaning that it will work in whatever channel you put it in.  I have these stored as code in my global templates so I can just click and drag it over.

Remember to edit the To, CC and From variables (lines 31 to 33) in the post processor and to have an SMTP server set up in your Mirth Connect settings.

I’m currently working on a channel that will email the user the current statistics on the channel if they reply to the alert email.  You will need the Email Receiver that comes with Mirth Support packages

5 Comments

Filed under Mirth Integration Engine, Tips and Tricks

Using HL7 for more than just Integration

When I first started in medical integration I realized that when our vendor quoted us for a new integration suite that an HL7 ADT feed was included each and every time.   I thought that was odd as this same vendor had already provided us with two individual ADT streams (one in V2.2,  and the other in V2.4).   When I asked it was explained that each receiving system required its own ADT feed.   Naturally this is the case when dealing with several different systems,  but modern Integration Engines eliminate the need for redundant feeds from your HIS (Hospital Information System),  or EHR (Electronic Health Record).
Over the years whenever we implemented a project that required an ADT feed we used our Mirth Integration Engine to send a copy of an existing feed to the new interface.   Mirth allows us to do any customization or filtering for each system so in the end we save ourselves tons of money in purchasing and maintaining seperate ADT feeds from our HIS.

This is a pretty simple example of what I want to talk about in this entry.  With the right Integration Engine,  and enough data in your HL7 message you can easily use existing feeds as a unique and robust solution to day to day issues that come up.   Here are some of the things we’ve done at the Huron Perth Healthcare Alliance.

Enhanced Data Analysis and Reporting
At one point we were having a problem with guarantors not being properly associated in our HIS.   This was causing our analysts fits as they would have to spend hours trying to track down potential mis-matches.
Mirth’s versatility and powerful javascripting abilities allowed us to implement an interface that would watch our main ADT feed and an analysis on every message.   Business rules were drawn up to identify a potential mis-match and,  if a message met those parameters the patient details would be written into a database.     Every night just after midnight,  another Mirth channel would trigger,   read the database and email the analyst a list of potential problem accounts  reducing workload and catching potential problems before they became problems.
This functionality can be easily adapated to watch for any records meeting certain business rules.

Custom Data Repository
We needed a way for several applications (including Mirth) to be able to pull certain information from our HIS.   The problem is that our HIS doesn’t allow direct queries.   We were faced with writing a script to pull this information periodically,  thereby limiting our ability to pull real time data and adding a level of complexity to the solution,   or,  we could write everything to a database and use SQL to pull the information.
Again,  we used existing HL7 feeds from our HIS to populate and update a lighweight data repository.   This gave us real-time information in an easy to query format simplifying our reporting tasks and allowing us to provide better data to the users.

Downtime Reporting
Anyone working in healthcare has come to dread what happens when the HIS is unavailable for any reason.  Of course,  in an electronic environment this means that vital patient info is not available.
Again,  using the Mirth Integration Engine and an existing HL7 results feed that gives us every test result and dictated note entered into the system we devised a solution.   Every report is converted into a human readable PDF.   This PDF is then picked up by another Mirth channel which determines what nursing unit the patient report belongs to and will write it o a folder on a PC on that floor.   This way the patient information is available to staff on the floor in the event of an HIS or network downtime.
Phase 2 of this project will involve moving patient records to another floor upon receipt of a patient transfer message,  or removing them if a patient discharge message is received.

These are just a few of the many functions a powerful integration engine such as Mirth will allow facilities to enhance the utility of HL7 messages coming from their systems.

Leave a comment

Filed under General Integration, Tips and Tricks