Thursday, 23 May 2013

Eclair document publisher aka I don't know what I'm doing

Your business wants to put documents into Eclair huh? They have an application making documents but they can't put them into an OBR message for Eclair to process for whatever reason?
There is this thing called the Eclair document publisher which is a windows service that will look in a directory for files, it will grab those files, parse the file name to extract metadata for the file and then cram them the document into the Eclair repository so the document can be viewed from the front end.
Someone has just given you the install for Eclair document publisher, you can manage that right? WRONG! YOU ARE LOOSE CANNON BALANCING ON THE EDGE OF THE ABYSS!

So anyway, this is what you'll have to do:

  1. Install that sucker
  2. Add configuration so it knows where to read the files and how to parse them

Things that will make you sad

  1. The EclairDocumentPublisher.exe is 32 bit, you're probably on a 64 bit machine that means things going in to the registry will be in the Wow6432Node hive rather than where the documentation says they will be
  2. Both EXEs (the actual service and the configuration app for the service) will try to update the registry, this means you probably need to run them as administrator (and when you install the service via command prompt, run the command prompt as administrator) otherwise there will be a stunning lack of results
  3. When we started the document publisher in our production environment our Eclair front-end performance degraded to a point where it was unusable. Whoops! Apparently the Doc Publisher adds some additional load to some database processes somehow, and that was enough to push our DB server over the edge and maxed out CPU and paging and memory was being written to disk or some hardware nerd stuff like that. This was fixed, as always, by adding more RAM to the virtual machine.
  4. I found the document publisher service had to be run as a user that is a local admin, may have related to the directory we were picking files up from and the security on that directory.
  5. If you screw up a configuration you might need delete the entire thing and start from scratch rather than trying to edit it. When a configuration is first created (or maybe when first seen by the other services) it seems to copy some information into the Eclair database, you can then no longer change that information via the configuration app.  I had this problem when I pointed it to the wrong IP address (pointed it to the Eclair server rather thann the Iris server)
  6. When you change the configuration for the Document Publisher you need to restart all of the  Eclair services for it to take effect, why, well 

Installing the Document Publisher windows service


Before you start, you're going to need all this junk:
  • EclairDocumentPublisher.exe
  • EclairDocumentExporterConfiguration.exe
  • 2_3.HDT
  • 2_3.HTT
  • ORUR012_3.HMT
  • A directory that your documents to process will be placed in
  • An example file with its filename ready for parsing, e.g. John_Labtest_NHI1234_19650429_4677929_C_158_ExamEndo_Upper GI endoscopy_20130314125148_Kelvin_Perrie_KPE001_20130318112338.pdf
  • There should already be an ODBC connection installed (I think the other services use this), you can verify this by running C:\Windows\SysWOW64\odbcad32.exe (not the one in control panel if you're on a 64 bit machine) and checking that an Eclair32 data source exists.
The EclairDocumentPublisher.exe is the actual code for the windows service. The EclairDocumentExporterConfiguration.exe is used to configure the windows service.  The other files, who knows, I think they are some sort of message schema documents that the service needs.

  1. Log on to your Eclair machine
  2. Copy the above files to the C:\Eclair\Services\ directory
  3. Put the example file into the directory where the service will be reading documents from
  4. Start a cmd prompt as administrator and navigate to C:\Eclair\Services
  5. Enter "EclairDocumentPublisher.exe /INSTALL EclairDocumentPublisher [Environment]" where [Environment] is one of TRAIN, TEST or PROD.  Our development environment points to eclair training.
  6. Confirm the service installed correctly by checking its settings are in the registory, Start -> Run -> RegEdit then navigate to HKEY_LOCAL_MACHINE\Software\WOW6432node\Delphic\ and confirm the Sub Key EclairDocumentPublisher exists here.  
  7. You will need to update the values in the registry to be correct for your environment.  The DBName should be set to your ODBC connection name.

Configuring the Document Publisher to process some documents


  1. Right click your EclairDocumentExporterConfiguration.exe and run as administrator
  2. Click next to create a new 'export'
  3. Select your example document and click next
  4. Enter your delimiter (we used an underscore) and it will preview the fields
  5. Map the values in your filename to fields in Eclair
  6. Click next and set non filename data
  7. Click next and enter your ODBC details
  8. Click next and enter the type of documents you want the publisher to grab
  9. Enter the details for the Eclair interface.  This is where the Iris services are, for us this is a different machine to where the Eclair services were.  The document publisher will pick up documents, make an HL7 out of them and pass it to the Iris service.
  10. Perfrom a test when asked, and give it a name and save.
  11. Confirm everything was saved correctly in the registry at the key HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Delphic\EclairDocumentPublishers\[The name of your configuration]
  12. Remove your example document so that when you start the document publisher service it isn't sent to Eclair
  13. Restart all of the Eclair services so that your changes to the configuration are picked up

All done yo.  Now when you drop a document into the folder it should get grabbed by the document publisher and put into Eclair.  The doc publisher service does have a log (location of the log is set in the registry), you can get more information in this log by setting the DEBUG value in the registry to 1.


Monday, 20 May 2013

The OBX segment is a jerk - how to use encapsulated data in an OBX segment

So say your hospital has this application that makes patient results (like the results after a procedure), and it does it in a PDF with embeded pictures and a nice layout and it looks all awesome. You want to get this PDF and jam it into your results repository so that the clinical staff can go to this single place to view results from different applications (if they have to log into multiple places they will get a severe case of the frowny faces). Now, it also turns out that your hospital has an 'enterprise messaging system' because that is the kind of insanity we like to get involved with in the health sector so that means you're using HL7 messages to pass information between applications.

Enter the OBR message. Ta da.

The OBR HL7 messages are used to send results, and the OBX segment of the message is what contains the actual results details.  Often they might just be holding that information as text, but you can also cram whole files into the OBX segment. The problem with the OBX is that it is a bit of a jerk and likes to be all mysterious and poorly documented by systems that both produce and consume OBR messages.

The second field in the OBX determines how you are sending your data. You've got options here; these seem to be the least useless options of putting data into an OBX:

RP = Reference pointer
ST = String
FT = Formatted text
TX = Text data
ED = Encapsulated data

The string, text and formatted text are sending the results as string data.  The reference pointer allows you send a link to a file (e.g. on a network drive).  Encapsulated data allows you to embed the entire contents of a file (e.g. a PDF or a DOCX) into the message.

A message with two sets of results (two OBX segments) where one is formatted text and one is encapsulated data looks like this:

MSH|[snip]
PID|[snip]
PV1|[snip]
ORC|[snip]
OBR|[snip]
OBX|0001|FT|1006^MCH^L^28539-5^^LN||"this is formatted text"|pg|||||I
OBX|0002|ED|1003^Haemoglobin^L^718-7^^LN||ClientFile^application^pdf^Base64^CBSL0xlbmd0aCA2Ny9QcmV2IDk0MjIvUm9vdCAxMSAwIFIvU2l6ZSAzMi9[snip]

So, the OBX which is set to encapsulated data ('ED') has the base64 encoded binary contents of a PDF jamed into the OBX.5 field.  That's kind of cool!

What these message actually look like on the recieving system I guess depends on how the application deals with them.  In our results repository (Eclair), if you send a single ED OBX (containing for example a PDF) it will open and display the document in panel as shown to the left.









If you send multiple OBX segments then the ED ones will show with clickable icons.













Yuuussssss.

Thursday, 9 May 2013

How to update a document in a SilentOne document repository


Document repositories!  Yay.  You put your document there so it is safe yet accessible.  You index it by the important bits of information.  Sometimes that information changes and now your document repository is out of date.  Stink for you! Luckily your document repository has an API where you can change the field values for given documents right?  Cool!  Do you have any documentation for that API?  Well, not if you're a SilentOne customer. 

The basic steps for locating a document for a given field value, updating that field to a new value and saving the change are pretty standard, but using the supplied API is not as straight forward as you would hope.

You've got to roll like this:
  1. Connect to the API
  2. Query the repository for documents with the field value you're looking for
  3. Iterate over the results
    1. Lock the document for editing
    2. Change the field to have its new value
    3. Checkin and unlock the document

Connect to the API 


_axisLibrary = new AxisLibraryService();

_axisLibrary.setUri(Library);
_axisLibrary.PreAuthenticate = true;
// this takes the user that can be authenticated in the silentone library
_axisLibrary.Credentials = new NetworkCredential(Username, Password, Domain);




where the values passed in are something like this:

<add key="silentOneUsername" value="svc-silentOne" />

<add key="silentOnePassword" value="thesecretpassword" />

<add key="silentOneDomain" value="OurDomain" />

<add key="silentOneLibrary" value="http://silentonedev/axis/services/PhoenixService" />


Query the repository for documents with the field value you're looking for


The method you're looking for is called 'executeQuery'.  This takes a path to search from (so you can just include a subset of the repository in your search) and a Query object.  The queryString property of the Query object is a bit of a mystery, if yours differs to the below you will need to ask SilentOne what the value should be.

ResultList resultList = _axisLibrary.executeQuery(LocationToSearch, new Query { queryString = string.Format(DocumentSearchQuery, oldValue) });



config values associated with this look something like:

<add key="silentOneDocumentSearchQuery" value="@NameOfFieldToUpdate = &quot;{0}&quot;" />

<add key="silentOneLocationToSearch" value="/Store/Records" />



Where it says 'NameOfFieldToUpdate' put the name of your field.  For example your query value might be:
@PatientNumber = "ABC1234"
This will return all documents where the PatientNumber field has a value of ABC1234.

Iterate over the results


The executeQuery method returns a ResultList object, which exposes an items collection, so you can just use a foreach.

foreach (var result in resultList.items)


Lock the document for editing


First, get the document.  I don't know of an easy way to do this from the ResultListItem that we already have.  So instead, we have to go back and request the object again based on the uri that the ResultListItem has.  Then call the lockObject method.  I don't know why the lockObject method returns a value.

Document document = (Document) _axisLibrary.getObject(result.uri);

var lockInfo = _axisLibrary.lockObject(document.uri, "Locking for update", 1);

Change the field to have its new value


On the document locate the field with a name that matches the field we want to update, then update the current value to the new value.

document.attributes.First(a => a.name == "NameOfFieldToUpdate").value = newValue;

Checkin and unlock the document


Call the checkin and then unlockObject methods.  On the checkin ensure that the 3rd parameter is null and the 4th parameter is 0.  I believe the 3rd is only not null when creating a new document, but I'm not sure - the combination below that works was found by many hours of painful trial and error.  The unlockObject method may or may not be required (I dunno, I'm not your dad, you figure it out).

document.contentInfo = _axisLibrary.checkin(document.uri, document, null, 0, string.Format("Bulk update process, changing value from {0} to {1}.", oldValue, newValue));

_axisLibrary.unlockObject(document.uri);



Job done!

Some things to note:
  • If you need to do a bulk update you will find that after a certain amount of document updates the API will crash.  It is always the same number, I think it was around 6000.  We did ours in batches of 3000.
  • You need a license file to access the API - get this from SilentOne