python-hl7 is a simple library for parsing messages of Health Level 7 (HL7) version 2.x into Python objects. python-hl7 includes a simple client that can send HL7 messages to a Minimal Lower Level Protocol (MLLP) server (mllp_send).
HL7 is a communication protocol and message format for health care data. It is the de-facto standard for transmitting data between clinical information systems and between clinical devices. The version 2.x series, which is often is a pipe delimited format is currently the most widely accepted version of HL7 (there is an alternative XML-based format).
python-hl7 currently only parses HL7 version 2.x messages into an easy to access data structure. The library could eventually also contain the ability to create HL7 v2.x messages.
python-hl7 parses HL7 into a series of wrapped hl7.Container objects. The there are specific subclasses of hl7.Container depending on the part of the HL7 message. The hl7.Container message itself is a subclass of a Python list, thus we can easily access the HL7 message as an n-dimensional list. Specifically, the subclasses of hl7.Container, in order, are hl7.Message, hl7.Segment, hl7.Field, hl7.Repetition. and hl7.Component.
0.3.0 breaks backwards compatibility by correcting the indexing of the MSH segment and the introducing improved parsing down to the repetition and sub-component level.
HL7 Messages have a limited number of levels. The top level is a Message. A Message is comprised of a number of Fields (hl7.Field). Fields can repeat (hl7.Repetition). The content of a field is either a primitive data type (such as a string) or a composite data type comprised of one or more Components (hl7.Component). Components are in turn comprised of Sub-Components (primitive data types).
The result of parsing is accessed as a tree using python list conventions:
The result can also be accessed using HL7 1-based indexing conventions by treating each element as a callable:
As an example, let’s create a HL7 message:
>>> message = 'MSH|^~\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4\r' >>> message += 'PID|||555-44-4444||EVERYWOMAN^EVE^E^^^^L|JONES|196203520|F|||153 FERNWOOD DR.^^STATESVILLE^OH^35292||(206)3345232|(206)752-121||||AC555444444||67-A4335^OH^20030520\r' >>> message += 'OBR|1|845439^GHH OE|1045813^GHH LAB|1554-5^GLUCOSE|||200202150730||||||||555-55-5555^PRIMARY^PATRICIA P^^^^MD^^LEVEL SEVEN HEALTHCARE, INC.|||||||||F||||||444-44-4444^HIPPOCRATES^HOWARD H^^^^MD\r' >>> message += 'OBX|1|SN|1554-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN||^182|mg/dl|70_105|H|||F'
We call the hl7.parse() command with string message:
>>> import hl7 >>> h = hl7.parse(message)
>>> type(h) <class 'hl7.containers.Message'>
We can always get the HL7 message back:
>>> unicode(h) == message True
Interestingly, hl7.Message can be accessed as a list:
>>> isinstance(h, list) True
There were 4 segments (MSH, PID, OBR, OBX):
>>> len(h) 4
>>> h [[u'OBX'], [u'1'], [u'SN'], [[[u'1554-5'], [u'GLUCOSE'], [u'POST 12H CFST:MCNC:PT:SER/PLAS:QN']]], [u''], [[[u''], [u'182']]], [u'mg/dl'], [u'70_105'], [u'H'], [u''], [u''], [u'F']] >>> h is h(4) True
Note that since the first element of the segment is the segment name, segments are effectively 1-based in python as well (because the HL7 spec does not count the segment name as part of the segment itself):
>>> h [u'OBX'] >>> h [u'1'] >>> h [u'SN'] >>> h(4)(2) [u'SN']
We can easily reconstitute this segment as HL7, using the appropriate separators:
>>> unicode(h) u'OBX|1|SN|1554-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN||^182|mg/dl|70_105|H|||F'
We can extract individual elements of the message:
>>> h u'GLUCOSE' >>> h is h(4)(3)(1)(2)(1) True >>> h u'182' >>> h is h(4)(5)(1)(2)(1) True
We can look up segments by the segment identifier, either via hl7.Message.segments() or via the traditional dictionary syntax:
>>> h.segments('OBX') u'GLUCOSE' >>> h['OBX'] u'GLUCOSE' >>> h['OBX'] is h['OBX'](1)(3)(1)(2)(1) True
Since many many types of segments only have a single instance in a message (e.g. PID or MSH), hl7.Message.segment() provides a convienance wrapper around hl7.Message.segments() that returns the first matching hl7.Segment:
>>> h.segment('PID') u'555-44-4444' >>> h.segment('PID') is h.segment('PID')(3)(1) True
The result of parsing contains up to 5 levels. The last level is a non-container type.
>>> type(h) <class 'hl7.containers.Message'> >>> type(h) <class 'hl7.containers.Segment'> >>> type(h) <class 'hl7.containers.Field'> >>> type(h) <class 'hl7.containers.Repetition'> >>> type(h) <class 'hl7.containers.Component'> >>> type(h) <type 'unicode'>
The parser only generates the levels which are present in the message.
>>> type(h) <class 'hl7.containers.Field'> >>> type(h) <type 'unicode'>
python-hl7 features a simple network client, mllp_send, which reads HL7 messages from a file or sys.stdin and posts them to an MLLP server. mllp_send is a command-line wrapper around hl7.client.MLLPClient. mllp_send is a useful tool for testing HL7 interfaces or resending logged messages:
mllp_send --file sample.hl7 --port 6661 mirth.example.com
See mllp_send - MLLP network client for examples and usage instructions.
For receiving HL7 messages using the Minimal Lower Level Protocol (MLLP), take a look at the related twisted-hl7 package. If do not want to use twisted and are looking to re-write some of twisted-hl7’s functionality, please reach out to us. It is likely that some of the MLLP parsing and formatting can be moved into python-hl7, which twisted-hl7 and other libraries can depend upon.
python-hl7 supports both Python 2.6+ and Python 3.3+. The library primarily deals in unicode (the str type in Python 3).
hl7.Message can be forced back into a string using unicode(message) in Python 2 and str(message) in Python 3.
mllp_send - MLLP network client assumes the stream is already in the correct encoding.