hl7-0.2.2/0000755000175000017500000000000011673147364013723 5ustar jpaulettjpaulett00000000000000hl7-0.2.2/tests/0000755000175000017500000000000011673147364015065 5ustar jpaulettjpaulett00000000000000hl7-0.2.2/tests/test_hl7.py0000644000175000017500000001234511627225652017171 0ustar jpaulettjpaulett00000000000000# -*- coding: utf-8 -*- from hl7 import Message, Segment, Field import hl7 import unittest ## Sample message from HL7 Normative Edition ## http://healthinfo.med.dal.ca/hl7intro/CDA_R2_normativewebedition/help/v3guide/v3guide.htm#v3gexamples sample_hl7 = u'\r'.join([ 'MSH|^~\&|GHH LAB|ELAB-3|GHH OE|BLDG4|200202150930||ORU^R01|CNTRL-3456|P|2.4', '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', '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', 'OBX|1|SN|1554-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN||^182|mg/dl|70_105|H|||F', 'OBX|2|FN|1553-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN||^182|mg/dl|70_105|H|||F\r' ]) class ParseTest(unittest.TestCase): def test_parse(self): msg = hl7.parse(sample_hl7) self.assertEqual(len(msg), 5) self.assertTrue(isinstance(msg[0][0][0], unicode)) self.assertEqual(msg[0][0][0], u'MSH') self.assertEqual(msg[3][0][0], u'OBX') self.assertEqual( msg[3][3], [u'1554-5', u'GLUCOSE', u'POST 12H CFST:MCNC:PT:SER/PLAS:QN'] ) def test_bytestring_converted_to_unicode(self): msg = hl7.parse(str(sample_hl7)) self.assertEqual(len(msg), 5) self.assertTrue(isinstance(msg[0][0][0], unicode)) self.assertEqual(msg[0][0][0], u'MSH') def test_non_ascii_bytestring(self): # \x96 - valid cp1252, not valid utf8 # it is the responsibility of the caller to convert to unicode self.assertRaises(UnicodeDecodeError, hl7.parse, 'MSH|^~\&|GHH LAB|ELAB\x963') def test_parsing_classes(self): msg = hl7.parse(sample_hl7) self.assertTrue(isinstance(msg, hl7.Message)) self.assertTrue(isinstance(msg[3], hl7.Segment)) self.assertTrue(isinstance(msg[3][0], hl7.Field)) self.assertTrue(isinstance(msg[3][0][0], unicode)) def test_nonstandard_separators(self): nonstd = 'MSH$%~\&$GHH LAB\rPID$$$555-44-4444$$EVERYWOMAN%EVE%E%%%L' msg = hl7.parse(nonstd) self.assertEqual(unicode(msg), nonstd) self.assertEqual(len(msg), 2) self.assertEqual(msg[1][5], ['EVERYWOMAN', 'EVE', 'E', '', '', 'L']) class IsHL7Test(unittest.TestCase): def test_ishl7(self): self.assertTrue(hl7.ishl7(sample_hl7)) def test_ishl7_empty(self): self.assertFalse(hl7.ishl7('')) def test_ishl7_None(self): self.assertFalse(hl7.ishl7(None)) def test_ishl7_wrongsegment(self): message = 'OBX|1|SN|1554-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN||^182|mg/dl|70_105|H|||F\r' self.assertFalse(hl7.ishl7(message)) class ContainerTest(unittest.TestCase): def test_unicode(self): msg = hl7.parse(sample_hl7) self.assertEqual(unicode(msg), sample_hl7.strip()) self.assertEqual( unicode(msg[3][3]), '1554-5^GLUCOSE^POST 12H CFST:MCNC:PT:SER/PLAS:QN' ) def test_container_unicode(self): c = hl7.Container('|') c.extend(['1', 'b', 'data']) self.assertEqual(unicode(c), '1|b|data') class MessageTest(unittest.TestCase): def test_segments(self): msg = hl7.parse(sample_hl7) s = msg.segments('OBX') self.assertEqual(len(s), 2) self.assertEqual(s[0][0:3], [['OBX'], ['1'], ['SN']]) self.assertEqual(s[1][0:3], [['OBX'], ['2'], ['FN']]) def test_segments_does_not_exist(self): msg = hl7.parse(sample_hl7) self.assertRaises(KeyError, msg.segments, 'BAD') def test_segment(self): msg = hl7.parse(sample_hl7) s = msg.segment('OBX') self.assertEqual(s[0:3], [['OBX'], ['1'], ['SN']]) def test_segment_does_not_exist(self): msg = hl7.parse(sample_hl7) self.assertRaises(KeyError, msg.segment, 'BAD') def test_segments_dict_key(self): msg = hl7.parse(sample_hl7) s = msg['OBX'] self.assertEqual(len(s), 2) self.assertEqual(s[0][0:3], [['OBX'], ['1'], ['SN']]) self.assertEqual(s[1][0:3], [['OBX'], ['2'], ['FN']]) class ParsePlanTest(unittest.TestCase): def test_create_parse_plan(self): plan = hl7.create_parse_plan(sample_hl7) self.assertEqual(plan.separators, ['\r', '|', '^']) self.assertEqual(plan.containers, [Message, Segment, Field]) def test_parse_plan(self): plan = hl7.create_parse_plan(sample_hl7) self.assertEqual(plan.separator, '\r') con = plan.container([1, 2]) self.assertTrue(isinstance(con, Message)) self.assertEqual(con, [1, 2]) self.assertEqual(con.separator, '\r') def test_parse_plan_next(self): plan = hl7.create_parse_plan(sample_hl7) n1 = plan.next() self.assertEqual(n1.separators, ['|', '^']) self.assertEqual(n1.containers, [Segment, Field]) n2 = n1.next() self.assertEqual(n2.separators, ['^']) self.assertEqual(n2.containers, [Field]) n3 = n2.next() self.assertTrue(n3 is None) if __name__ == '__main__': unittest.main() hl7-0.2.2/tests/test_client.py0000644000175000017500000001432111673147320017745 0ustar jpaulettjpaulett00000000000000from hl7.client import MLLPClient, MLLPException, mllp_send, CR, SB, EB from mock import patch, Mock from optparse import Values from shutil import rmtree from tempfile import mkdtemp import os import socket import unittest class MLLPClientTest(unittest.TestCase): def setUp(self): # use a mock version of socket self.socket_patch = patch('hl7.client.socket.socket') self.mock_socket = self.socket_patch.start() self.client = MLLPClient('localhost', 6666) def tearDown(self): # unpatch socket self.socket_patch.stop() def test_connect(self): self.mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) self.client.socket.connect.assert_called_once_with(('localhost', 6666)) def test_close(self): self.client.close() self.client.socket.close.assert_called_once_with() def test_send(self): self.client.socket.recv.return_value = 'thanks' result = self.client.send('foobar\n') self.assertEqual(result, 'thanks') self.client.socket.send.assert_called_once_with('foobar\n') self.client.socket.recv.assert_called_once_with(4096) def test_send_message(self): self.client.socket.recv.return_value = 'thanks' result = self.client.send_message('foobar') self.assertEqual(result, 'thanks') self.client.socket.send.assert_called_once_with('\x0bfoobar\x1c\x0d') def test_context_manager(self): with MLLPClient('localhost', 6666) as client: client.send('hello world') self.client.socket.send.assert_called_once_with('hello world') self.client.socket.close.assert_called_once_with() def test_context_manager_exception(self): try: with MLLPClient('localhost', 6666): raise Exception() self.fail() except: # expected pass # socket.close should be called via the with statement self.client.socket.close.assert_called_once_with() class MLLPSendTest(unittest.TestCase): def setUp(self): # patch to avoid touching sys and socket self.socket_patch = patch('hl7.client.socket.socket') self.mock_socket = self.socket_patch.start() self.mock_socket().recv.return_value = 'thanks' self.stdout_patch = patch('hl7.client.stdout') self.mock_stdout = self.stdout_patch.start() self.stdin_patch = patch('hl7.client.stdin') self.mock_stdin = self.stdin_patch.start() self.stderr_patch = patch('hl7.client.stderr') self.mock_stderr = self.stderr_patch.start() # we need a temporary directory self.dir = mkdtemp() self.write(SB + 'foobar' + EB + CR) self.option_values = Values({ 'port': 6661, 'filename': os.path.join(self.dir, 'test.hl7'), 'verbose': True, 'loose': False, }) self.options_patch = patch('hl7.client.OptionParser') option_parser = self.options_patch.start() self.mock_options = Mock() option_parser.return_value = self.mock_options self.mock_options.parse_args.return_value = (self.option_values, ['localhost']) def tearDown(self): # unpatch self.socket_patch.stop() self.options_patch.stop() self.stdout_patch.stop() self.stdin_patch.stop() self.stderr_patch.stop() # clean up the temp directory rmtree(self.dir) def write(self, content, path='test.hl7'): with open(os.path.join(self.dir, path), 'w') as f: f.write(content) def test_send(self): mllp_send() self.mock_socket().connect.assert_called_once_with(('localhost', 6661)) self.mock_socket().send.assert_called_once_with(SB + 'foobar' + EB + CR) self.mock_stdout.assert_called_once_with('thanks') def test_send_mutliple(self): self.mock_socket().recv.return_value = 'thanks' self.write(SB + 'foobar' + EB + CR + SB + 'hello' + EB + CR) mllp_send() self.assertEqual(self.mock_socket().send.call_args_list[0][0][0], SB + 'foobar' + EB + CR) self.assertEqual(self.mock_socket().send.call_args_list[1][0][0], SB + 'hello' + EB + CR) def test_leftover_buffer(self): self.write(SB + 'foobar' + EB + CR + SB + 'stuff') self.assertRaises(MLLPException, mllp_send) self.mock_socket().send.assert_called_once_with(SB + 'foobar' + EB + CR) def test_quiet(self): self.option_values.verbose = False mllp_send() self.mock_socket().send.assert_called_once_with(SB + 'foobar' + EB + CR) self.assertFalse(self.mock_stdout.called) def test_port(self): self.option_values.port = 7890 mllp_send() self.mock_socket().connect.assert_called_once_with(('localhost', 7890)) def test_stdin(self): self.option_values.filename = None self.mock_stdin.return_value = FakeStream() mllp_send() self.mock_socket().send.assert_called_once_with(SB + 'hello' + EB + CR) def test_loose_no_stdin(self): self.option_values.loose = True self.option_values.filename = None self.mock_stdin.return_value = FakeStream() mllp_send() self.assertFalse(self.mock_socket().send.called) self.mock_stderr().write.assert_call_with( '--loose requires --file with a single message\n' ) def test_loose_windows_newline(self): self.option_values.loose = True self.write(SB + 'foo\r\nbar\r\n' + EB + CR) mllp_send() self.mock_socket().send.assert_called_once_with(SB + 'foo\rbar' + EB + CR) def test_loose_no_mllp_characters(self): self.option_values.loose = True self.write('foo\r\nbar\r\n') mllp_send() self.mock_socket().send.assert_called_once_with(SB + 'foo\rbar' + EB + CR) class FakeStream(object): count = 0 def read(self, buf): self.count += 1 if self.count == 1: return SB + 'hello' + EB + CR else: return '' hl7-0.2.2/tests/__init__.py0000644000175000017500000000000011607166577017170 0ustar jpaulettjpaulett00000000000000hl7-0.2.2/README.rst0000644000175000017500000000040111607111104015361 0ustar jpaulettjpaulett00000000000000python-hl7 is a simple library for parsing messages of Health Level 7 (HL7) version 2.x into Python objects. * Source Code: http://github.com/johnpaulett/python-hl7 * Documentation: http://python-hl7.readthedocs.org * PyPi: http://pypi.python.org/pypi/hl7hl7-0.2.2/setup.cfg0000644000175000017500000000007311673147364015544 0ustar jpaulettjpaulett00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 hl7-0.2.2/hl7.egg-info/0000755000175000017500000000000011673147364016107 5ustar jpaulettjpaulett00000000000000hl7-0.2.2/hl7.egg-info/entry_points.txt0000644000175000017500000000006411673147364021405 0ustar jpaulettjpaulett00000000000000[console_scripts] mllp_send = hl7.client:mllp_send hl7-0.2.2/hl7.egg-info/SOURCES.txt0000644000175000017500000000071711673147364020000 0ustar jpaulettjpaulett00000000000000LICENSE MANIFEST.in README.rst setup.py docs/.gitignore docs/Makefile docs/api.rst docs/authors.rst docs/changelog.rst docs/conf.py docs/contribute.rst docs/index.rst docs/license.rst docs/make.bat docs/mllp_send.rst hl7/__init__.py hl7/client.py hl7.egg-info/PKG-INFO hl7.egg-info/SOURCES.txt hl7.egg-info/dependency_links.txt hl7.egg-info/entry_points.txt hl7.egg-info/top_level.txt hl7.egg-info/zip-safe tests/__init__.py tests/test_client.py tests/test_hl7.pyhl7-0.2.2/hl7.egg-info/PKG-INFO0000644000175000017500000000210511673147364017202 0ustar jpaulettjpaulett00000000000000Metadata-Version: 1.0 Name: hl7 Version: 0.2.2 Summary: Python library parsing HL7 v2.x messages Home-page: http://python-hl7.readthedocs.org Author: John Paulett Author-email: john -at- paulett.org License: BSD Description: python-hl7 is a simple library for parsing messages of Health Level 7 (HL7) version 2.x into Python objects. * Documentation: http://python-hl7.readthedocs.org * Source Code: http://github.com/johnpaulett/python-hl7 Keywords: HL7,Health Level 7,healthcare,health care,medical record Platform: POSIX Platform: Windows Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Healthcare Industry Classifier: Topic :: Communications Classifier: Topic :: Scientific/Engineering :: Medical Science Apps. Classifier: Topic :: Software Development :: Libraries :: Python Modules hl7-0.2.2/hl7.egg-info/top_level.txt0000644000175000017500000000000411673147364020633 0ustar jpaulettjpaulett00000000000000hl7 hl7-0.2.2/hl7.egg-info/dependency_links.txt0000644000175000017500000000000111673147364022155 0ustar jpaulettjpaulett00000000000000 hl7-0.2.2/hl7.egg-info/zip-safe0000644000175000017500000000000111607067021017523 0ustar jpaulettjpaulett00000000000000 hl7-0.2.2/PKG-INFO0000644000175000017500000000210511673147364015016 0ustar jpaulettjpaulett00000000000000Metadata-Version: 1.0 Name: hl7 Version: 0.2.2 Summary: Python library parsing HL7 v2.x messages Home-page: http://python-hl7.readthedocs.org Author: John Paulett Author-email: john -at- paulett.org License: BSD Description: python-hl7 is a simple library for parsing messages of Health Level 7 (HL7) version 2.x into Python objects. * Documentation: http://python-hl7.readthedocs.org * Source Code: http://github.com/johnpaulett/python-hl7 Keywords: HL7,Health Level 7,healthcare,health care,medical record Platform: POSIX Platform: Windows Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Healthcare Industry Classifier: Topic :: Communications Classifier: Topic :: Scientific/Engineering :: Medical Science Apps. Classifier: Topic :: Software Development :: Libraries :: Python Modules hl7-0.2.2/MANIFEST.in0000644000175000017500000000016011607136070015443 0ustar jpaulettjpaulett00000000000000include LICENSE README.rst include *.py include tests/*.py include docs/** exclude docs/_build global-exclude *~hl7-0.2.2/LICENSE0000644000175000017500000000263411607112755014726 0ustar jpaulettjpaulett00000000000000Copyright (C) 2009-2011 John Paulett (john -at- paulett.org) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. hl7-0.2.2/setup.py0000755000175000017500000000233711627301552015433 0ustar jpaulettjpaulett00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from setuptools import setup import hl7 as _hl7 setup( name = 'hl7', version = _hl7.__version__, description = 'Python library parsing HL7 v2.x messages', long_description = _hl7.__doc__, author = _hl7.__author__, author_email = _hl7.__email__, url = _hl7.__url__, license = _hl7.__license__, platforms = ['POSIX', 'Windows'], keywords = ['HL7', 'Health Level 7', 'healthcare', 'health care', 'medical record'], classifiers = [ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'Intended Audience :: Healthcare Industry', 'Topic :: Communications', 'Topic :: Scientific/Engineering :: Medical Science Apps.', 'Topic :: Software Development :: Libraries :: Python Modules', ], packages = ['hl7'], test_suite = 'tests', tests_require = ['mock'], entry_points = { 'console_scripts': [ 'mllp_send = hl7.client:mllp_send', ], }, zip_safe=True, ) hl7-0.2.2/hl7/0000755000175000017500000000000011673147364014415 5ustar jpaulettjpaulett00000000000000hl7-0.2.2/hl7/client.py0000644000175000017500000001161311673147320016237 0ustar jpaulettjpaulett00000000000000from optparse import OptionParser import os.path import socket import sys SB = '\x0b' #, vertical tab EB = '\x1c' #, file separator CR = '\x0d' #, \r FF = '\x0c' # , new page form feed RECV_BUFFER = 4096 class MLLPException(Exception): pass class MLLPClient(object): """ A basic, blocking, HL7 MLLP client based upon :py:mod:`socket`. MLLPClient implements two methods for sending data to the server. * :py:meth:`MLLPClient.send` for raw data that already is wrapped in the appropriate MLLP container (e.g. *message*). * :py:meth:`MLLPClient.send_message` will wrap the message in the MLLP container Can be used by the ``with`` statement to ensure :py:meth:`MLLPClient.close` is called:: with MLLPClient(host, port) as client: client.send_message('MSH|...') """ def __init__(self, host, port): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((host, port)) def __enter__(self): return self def __exit__(self, exc_type, exc_val, trackeback): self.close() def close(self): """Release the socket connection""" self.socket.close() def send_message(self, message): """Wraps a str, unicode, or :py:cls:`hl7.Message` in a MLLP container and send the message to the server """ # wrap in MLLP message container data = SB + unicode(message) + EB + CR # TODO consider encoding (e.g. UTF-8) return self.send(data) def send(self, data): """Low-level, direct access to the socket.send (data must be already wrapped in an MLLP container). Blocks until the server returns. """ # upload the data self.socket.send(data) # wait for the ACK/NACK return self.socket.recv(RECV_BUFFER) # wrappers to make testing easier def stdout(content): sys.stdout.write(content + '\n') def stdin(): return sys.stdin def stderr(): return sys.stderr def read_stream(stream): """Buffer the stream and yield individual, stripped messages""" _buffer = '' while True: data = stream.read(RECV_BUFFER) if data == '': break # usually should be broken up by EB, but I have seen FF separating # messages messages = (_buffer + data).split(EB if FF not in data else FF) # whatever is in the last chunk is an uncompleted message, so put back # into the buffer _buffer = messages.pop(-1) for m in messages: yield m.strip(SB+CR) if len(_buffer.strip()) > 0: raise MLLPException('buffer not terminated: %s' % _buffer) def read_loose(stream): """Turn a single HL7-like blob of text into a real HL7 message""" # load all the data (should only be 1 message) data = stream.read() # Windows new lines to segment separators data = data.replace('\r\n', '\r') # take out all the the typical MLLP separators and trailing # whitespace data = data.strip(EB+FF+SB+CR+'\n ') yield data def mllp_send(): """Command line tool to send messages to an MLLP server""" # set up the command line options script_name = os.path.basename(sys.argv[0]) parser = OptionParser(usage=script_name + ' [options] ') parser.add_option('-p', '--port', action='store', type='int', dest='port', default=6661, help='port to connect to') parser.add_option('-f', '--file', dest='filename', help='read from FILE instead of stdin', metavar='FILE') parser.add_option('-q', '--quiet', action='store_true', dest='verbose', default=True, help='do not print status messages to stdout') parser.add_option('--loose', action='store_true', dest='loose', default=False, help='allow file to be a HL7-like object (\\r\\n ' \ + 'instead of \\r). Can ONLY send 1 message. Requires ' \ + '--file option (no stdin)') (options, args) = parser.parse_args() if len(args) == 1: host = args[0] else: # server not present parser.print_usage() stderr().write('server required\n') return if options.filename is not None: stream = open(options.filename, 'rb') #FIXME with_statement else: if options.loose: stderr().write('--loose requires --file with a single message\n') return stream = stdin() with MLLPClient(host, options.port) as client: message_stream = read_stream(stream) \ if not options.loose \ else read_loose(stream) for message in message_stream: result = client.send_message(message) if options.verbose: stdout(result) if __name__ == '__main__': mllp_send() hl7-0.2.2/hl7/__init__.py0000644000175000017500000001745411673147330016532 0ustar jpaulettjpaulett00000000000000# -*- coding: utf-8 -*- """python-hl7 is a simple library for parsing messages of Health Level 7 (HL7) version 2.x into Python objects. * Documentation: http://python-hl7.readthedocs.org * Source Code: http://github.com/johnpaulett/python-hl7 """ __version__ = '0.2.2' __author__ = 'John Paulett' __email__ = 'john -at- paulett.org' __license__ = 'BSD' __copyright__ = 'Copyright 2011, John Paulett ' __url__ = 'http://python-hl7.readthedocs.org' def ishl7(line): """Determines whether a *line* looks like an HL7 message. This method only does a cursory check and does not fully validate the message. :rtype: bool """ ## Prevent issues if the line is empty return line.strip().startswith('MSH') if line else False def parse(line): """Returns a instance of the :py:class:`hl7.Message` that allows indexed access to the data elements. .. note:: HL7 usually contains only ASCII, but can use other character sets (HL7 Standards Document, Section 1.7.1). Therefore, python-hl7 works on Python unicode strings. :py:func:`hl7.parse` will accept ASCII-only strings and automatically convert them into unicode. However, if the message contains non-ASCII characters, it is the responsibility of the caller of :py:func:`hl7.parse` to properly convert the message string to unicode first. >>> h = hl7.parse(message) :rtype: :py:class:`hl7.Message` """ ## ensure that we get unicode input. For regular ASCII, the conversion ## will occur seamlessly, but for non-ASCII strings, parse must receive ## a unicode string or it will error out line = unicode(line) ## Strip out unnecessary whitespace strmsg = line.strip() ## The method for parsing the message plan = create_parse_plan(strmsg) ## Start spliting the methods based upon the ParsePlan return _split(strmsg, plan) def _split(text, plan): """Recursive function to split the *text* into an n-deep list, according to the :py:class:`hl7._ParsePlan`. """ ## Base condition, if we have used up all the plans if not plan: return text ## Recurse so that the sub plans are used in order to split the data ## into the approriate type as defined by the current plan. data = [_split(x, plan.next()) for x in text.split(plan.separator)] ## Return the instance of the current message part according ## to the plan return plan.container(data) class Container(list): """Abstract root class for the parts of the HL7 message.""" def __init__(self, separator, sequence=[]): ## Initialize the list object, optionally passing in the ## sequence. Since list([]) == [], using the default ## parameter will not cause any issues. super(Container, self).__init__(sequence) self.separator = separator def __unicode__(self): """Join a the child containers into a single string, separated by the self.separator. This method acts recursively, calling the children's __unicode__ method. Thus ``unicode()`` is the approriate method for turning the python-hl7 representation of HL7 into a standard string. >>> unicode(h) == message True """ return self.separator.join((unicode(x) for x in self)) class Message(Container): """Representation of an HL7 message. It contains a list of :py:class:`hl7.Segment` instances. """ def __getitem__(self, key): """Index or segment-based lookup. If key is an integer, ``__getitem__`` acts list a list, returning the :py:class:`hl7.Segment` held at that index: >>> h[1] [[u'PID'], ...] If the key is a string, ``__getitem__`` acts like a dictionary, returning all segments whose *segment_id* is *key* (alias of :py:meth:`hl7.Message.segments`). >>> h['OBX'] [[[u'OBX'], [u'1'], ...]] :rtype: :py:class:`hl7.Segment` or list of :py:class:`hl7.Segment` """ if isinstance(key, basestring): return self.segments(key) return list.__getitem__(self, key) def segment(self, segment_id): """Gets the first segment with the *segment_id* from the parsed *message*. >>> h.segment('PID') [[u'PID'], ...] :rtype: :py:class:`hl7.Segment` """ ## Get the list of all the segments and pull out the first one, ## if possible match = self.segments(segment_id) ## We should never get an IndexError, since segments will instead ## throw an KeyError return match[0] def segments(self, segment_id): """Returns the requested segments from the parsed *message* that are identified by the *segment_id* (e.g. OBR, MSH, ORC, OBX). >>> h.segments('OBX') [[[u'OBX'], [u'1'], ...]] :rtype: list of :py:class:`hl7.Segment` """ ## Compare segment_id to the very first string in each segment, ## returning all segments that match matches = [segment for segment in self if segment[0][0] == segment_id] if len(matches) == 0: raise KeyError('No %s segments' % segment_id) return matches class Segment(Container): """Second level of an HL7 message, which represents an HL7 Segment. Traditionally this is a line of a message that ends with a carriage return and is separated by pipes. It contains a list of :py:class:`hl7.Field` instances. """ class Field(Container): """Third level of an HL7 message, that traditionally is surrounded by pipes and separated by carets. It contains a list of strings. """ def create_parse_plan(strmsg): """Creates a plan on how to parse the HL7 message according to the details stored within the message. """ ## We will always use a carriage return to separate segments separators = ['\r'] ## Parse out the other separators from the characters following ## MSH. Currently we only go two-levels deep and ignore some ## details. separators.extend(list(strmsg[3:5])) ## The ordered list of containers to create containers = [Message, Segment, Field] return _ParsePlan(separators, containers) class _ParsePlan(object): """Details on how to parse an HL7 message. Typically this object should be created via :func:`hl7.create_parse_plan` """ # field, component, repetition, escape, subcomponent # TODO implement escape, and subcomponent def __init__(self, separators, containers): # TODO test to see performance implications of the assertion # since we generate the ParsePlan, this should never be in # invalid state assert len(containers) == len(separators) self.separators = separators self.containers = containers @property def separator(self): """Return the current separator to use based on the plan.""" return self.separators[0] def container(self, data): """Return an instance of the approriate container for the *data* as specified by the current plan. """ return self.containers[0](self.separator, data) def next(self): """Generate the next level of the plan (essentially generates a copy of this plan with the level of the container and the seperator starting at the next index. """ if len(self.containers) > 1: ## Return a new instance of this class using the tails of ## the separators and containers lists. Use self.__class__() ## in case :class:`hl7.ParsePlan` is subclassed return self.__class__(self.separators[1:], self.containers[1:]) ## When we have no separators and containers left, return None, ## which indicates that we have nothing further. return None hl7-0.2.2/docs/0000755000175000017500000000000011673147364014653 5ustar jpaulettjpaulett00000000000000hl7-0.2.2/docs/license.rst0000644000175000017500000000007011607112333017005 0ustar jpaulettjpaulett00000000000000License ======= .. include:: ../LICENSE :literal: hl7-0.2.2/docs/contribute.rst0000644000175000017500000000132411673147320017553 0ustar jpaulettjpaulett00000000000000Contributing ============ The source code is available at http://github.com/johnpaulett/python-hl7 Please fork and issue pull requests. Generally any changes, bug fixes, or new features should be accompanied by corresponding tests in our test suite. Testing -------- The test suite is located in :file:`tests/` and can be run via :file:`setup.py`:: $ python setup.py test ... ---------------------------------------------------------------------- Ran 17 tests in 0.005s OK Make sure the documentation is still valid:: $ pushd docs && make html man doctest && popd ... Doctest summary =============== 23 tests 0 failures in tests 0 failures in setup code ... hl7-0.2.2/docs/index.rst0000644000175000017500000001262611673147320016513 0ustar jpaulettjpaulett00000000000000python-hl7 - Easy HL7 v2.x Parsing ================================== 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 (:ref:`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 (version 3.0 is an XML-based format). python-hl7 currently only parses HL7 version 2.x messages into an easy to access data structure. The current implementation does not completely follow the HL7 specification, but is good enough to parse the most commonly seen HL7 messages. The library could potentially evolve into being fully complainant with the spec. The library could eventually also contain the ability to create HL7 v2.x messages. python-hl7 parses HL7 into a series of wrapped :py:class:`hl7.Container` objects. The there are specific subclasses of :py:class:`hl7.Container` depending on the part of the HL7 message. The :py:class:`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 :py:class:`hl7.Container`, in order, are :py:class:`hl7.Message`, :py:class:`hl7.Segment`, and :py:class:`hl7.Field`. Eventually additional containers will be added to fully support the HL7 specification. Usage ----- As an example, let's create a HL7 message: .. doctest:: >>> 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 :py:func:`hl7.parse` command with string message: .. doctest:: >>> import hl7 >>> h = hl7.parse(message) We get a :py:class:`hl7.Message` object, wrapping a series of :py:class:`hl7.Segment` objects: .. doctest:: >>> type(h) We can always get the HL7 message back: .. doctest:: >>> unicode(h) == message True Interestingly, :py:class:`hl7.Message` can be accessed as a list: .. doctest:: >>> isinstance(h, list) True There were 4 segments (MSH, PID, OBR, OBX): .. doctest:: >>> len(h) 4 We can extract the :py:class:`hl7.Segment` from the :py:class:`hl7.Message` instance: .. doctest:: >>> h[3] [[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']] We can easily reconstitute this segment as HL7, using the appropriate separators: .. doctest:: >>> unicode(h[3]) 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: .. doctest:: >>> h[3][3][1] u'GLUCOSE' >>> h[3][5][1] u'182' We can look up segments by the segment identifier, either via :py:meth:`hl7.Message.segments` or via the traditional dictionary syntax: .. doctest:: >>> h.segments('OBX')[0][3][1] u'GLUCOSE' >>> h['OBX'][0][3][1] u'GLUCOSE' Since many many types of segments only have a single instance in a message (e.g. PID or MSH), :py:meth:`hl7.Message.segment` provides a convienance wrapper around :py:meth:`hl7.Message.segments` that returns the first matching :py:class:`hl7.Segment`: .. doctest:: >>> h.segment('PID')[3][0] u'555-44-4444' MLLP network client - ``mllp_send`` ----------------------------------- 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 :py:class:`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 :doc:`mllp_send` for examples and usage instructions. Contents -------- .. toctree:: :maxdepth: 1 api mllp_send contribute changelog authors license Install ------- python-hl7 is available on `PyPi `_ via ``pip`` or ``easy_install``:: pip install -U hl7 For recent versions of Debian and Ubuntu, the *python-hl7* package is available:: sudo apt-get install python-hl7 Links ----- * Documentation: http://python-hl7.readthedocs.org * Source Code: http://github.com/johnpaulett/python-hl7 * PyPi: http://pypi.python.org/pypi/hl7 HL7 References: * `Health Level 7 - Wikipedia `_ * `nule.org's Introduction to HL7 `_ * `hl7.org `_ * `OpenMRS's HL7 documentation `_ * `Transport Specification: MLLP `_ hl7-0.2.2/docs/.gitignore0000644000175000017500000000001111607111104016607 0ustar jpaulettjpaulett00000000000000/_build/ hl7-0.2.2/docs/api.rst0000644000175000017500000000200711627301552016143 0ustar jpaulettjpaulett00000000000000python-hl7 API ============== .. testsetup:: * import hl7 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' .. autofunction:: hl7.parse .. autofunction:: hl7.ishl7 Data Types ---------- .. autoclass:: hl7.Container :members: __unicode__ .. autoclass:: hl7.Message :members: segments, segment, __getitem__ .. autoclass:: hl7.Segment .. autoclass:: hl7.Field MLLP Network Client ------------------- .. autoclass:: hl7.client.MLLPClient :members: send_message, send, close hl7-0.2.2/docs/authors.rst0000644000175000017500000000006711610123073017053 0ustar jpaulettjpaulett00000000000000Authors ======= .. include:: ../AUTHORS :literal: hl7-0.2.2/docs/conf.py0000644000175000017500000002027211673147320016145 0ustar jpaulettjpaulett00000000000000# -*- coding: utf-8 -*- # # python-hl7 documentation build configuration file, created by # sphinx-quickstart on Tue Jul 12 10:57:30 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) import hl7 # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'python-hl7' copyright = u'2011, John Paulett' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = hl7.__version__ # The full version, including alpha/beta/rc tags. release = hl7.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinxdoc' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'python-hl7doc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'python-hl7.tex', u'python-hl7 Documentation', u'John Paulett', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('mllp_send', 'mllp_send', 'MLLP network client', [u'John Paulett'], 1) ] # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'python-hl7' epub_author = u'John Paulett' epub_publisher = u'John Paulett' epub_copyright = u'2011, John Paulett' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. #epub_exclude_files = [] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} hl7-0.2.2/docs/Makefile0000644000175000017500000001077611607111104016302 0ustar jpaulettjpaulett00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-hl7.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-hl7.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/python-hl7" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/python-hl7" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." hl7-0.2.2/docs/make.bat0000644000175000017500000001064711607111104016244 0ustar jpaulettjpaulett00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\python-hl7.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\python-hl7.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end hl7-0.2.2/docs/changelog.rst0000644000175000017500000000237211673147330017331 0ustar jpaulettjpaulett00000000000000Change Log ========== 0.2.2 - 2011-12-17 ------------------ * :ref:`mllp_send ` now takes the ``--loose`` options, which allows sending HL7 messages that may not exactly meet the standard (Windows newlines separating segments instead of carriage returns). 0.2.1 - 2011-08-30 ------------------ * Added MLLP client (:py:class:`hl7.client.MLLPClient`) and command line tool, :ref:`mllp_send `. 0.2.0 - 2011-06-12 ------------------ * Converted ``hl7.segment`` and ``hl7.segments`` into methods on :py:class:`hl7.Message`. * Support dict-syntax for getting Segments from a Message (e.g. ``message['OBX']``) * Use unicode throughout python-hl7 since the HL7 spec allows non-ASCII characters. It is up to the caller of :py:func:`hl7.parse` to convert non-ASCII messages into unicode. * Refactored from single hl7.py file into the hl7 module. * Added Sphinx `documentation `_. Moved project to `github `_. 0.1.1 - 2009-06-27 ------------------ * Apply Python 3 trove classifier 0.1.0 - 2009-03-13 ------------------ * Support message-defined separation characters * Message, Segment, Field classes 0.0.3 - 2009-01-09 ------------------ * Initial release hl7-0.2.2/docs/mllp_send.rst0000644000175000017500000000370311673147320017355 0ustar jpaulettjpaulett00000000000000=================================== ``mllp_send`` - MLLP network client =================================== 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 :py:class:`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 MSH|^~\&|LIS|Example|Hospital|Mirth|20111207105244||ACK^A01|A234244|P|2.3.1| MSA|AA|234242|Message Received Successfully| Usage ===== :: Usage: mllp_send [options] Options: -h, --help show this help message and exit -p PORT, --port=PORT port to connect to -f FILE, --file=FILE read from FILE instead of stdin -q, --quiet do not print status messages to stdout --loose allow file to be a HL7-like object (\r\n instead of \r). Can ONLY send 1 message. Requires --file option (no stdin) Input Format ============ By default, ``mllp_send`` expects the ``FILE`` or stdin input to be a properly formatted HL7 message (carriage returns separating segments) wrapped in a MLLP stream (``message1message2...``). However, it is common, especially if the file has been manually edited in certain text editors, that the ASCII control characters will be lost and the carriage returns will be replaced with the platform's default line endings. In this case, ``mllp_send`` provides the ``--loose`` option, which attempts to take something that "looks like HL7" and convert it into a proper HL7 message. Currently the ``--loose`` option can only handle 1 HL7 message per file (it causes ``mllp_send`` to assume the whole file is one HL7 message). Additional Resources ==================== * http://python-hl7.readthedocs.org