Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 2 commits
  • 6 files changed
  • 0 comments
  • 1 contributor
Mar 14, 2012
John Paulett Use unicode internally. Receiver now must implement getCodec.
Speak byte strings with source system, but use unicode internally and
pass unicode messages to the receiver implementation.

Previously, for high-order cp1252 encoded messages, the ACK's
hl7.parse() was causing the server to permanently stop ACK'ing as
soon as it received the such a message.
1165b07
John Paulett Documentation for 0.0.3 release. 6e2a89c
8 docs/changelog.rst
Source Rendered
@@ -2,6 +2,14 @@
2 2 Change Log
3 3 ==========
4 4
  5 +0.0.3 - March 2012
  6 +==================
  7 +
  8 +* Convert to unicode. As soon as a message string is assembled, decode into
  9 + unicode, using the codec specified by the implementation of
  10 + ``IHL7Receiver.getCodec()`. When writing an ACK, the message is re-encoded
  11 + into that codec.
  12 +
5 13 0.0.2 - September 2011
6 14 ======================
7 15
2  setup.py
@@ -2,7 +2,7 @@
2 2
3 3 setup(
4 4 name='twisted-hl7',
5   - version='0.0.2',
  5 + version='0.0.3',
6 6 author='John Paulett',
7 7 author_email = 'john@paulett.org',
8 8 url = 'http://twisted-hl7.readthedocs.org',
9 tests/test_ack.py
@@ -21,3 +21,12 @@ def test_ae(self):
21 21 expected = 'MSH|^~\\&|GHH OE|BLDG4|GHH LAB|ELAB-3|200202150930||ACK^001|CNTRL-3456|P|2.4\rMSA|AE|CNTRL-3456'
22 22
23 23 self.assertEqual(expected, result)
  24 +
  25 + def test_unicode(self):
  26 + # ACK takes and returns unicode. The Factory is responsible for
  27 + # decoding/encoding
  28 + message = unicode(HL7_MESSAGE).replace(u'BLDG4', u'x\u201ay')
  29 + result = ACK(message)
  30 +
  31 + expected = u'MSH|^~\\&|GHH OE|x\u201ay|GHH LAB|ELAB-3|200202150930||ACK^001|CNTRL-3456|P|2.4\rMSA|AA|CNTRL-3456'
  32 + self.assertEqual(expected, result)
14 tests/test_mllp.py
@@ -22,6 +22,9 @@ def handleMessage(self, message):
22 22 self.messages.append(message)
23 23 return defer.succeed(ACK(message, self.ack_code))
24 24
  25 + def getCodec(self):
  26 + return 'cp1252'
  27 +
25 28 class MinimalLowerLayerProtocolTest(TestCase):
26 29 def setUp(self):
27 30 self.receiver = CaptureReceiver()
@@ -46,3 +49,14 @@ def testUncaughtError(self):
46 49
47 50 self.assertEqual(self.protocol.transport.write.call_args[0][0],
48 51 '\x0b' + EXPECTED_ACK.format('AR') + '\x1c\x0d')
  52 +
  53 + def testParseMessageUnicode(self):
  54 + message = HL7_MESSAGE.replace('BLDG4', 'x\x82y')
  55 + self.protocol.dataReceived('\x0b' + message + '\x1c\x0d')
  56 +
  57 + expected_message = unicode(HL7_MESSAGE).replace(u'BLDG4', u'x\u201ay')
  58 + self.assertEqual(self.receiver.messages, [expected_message])
  59 +
  60 + expected_ack = EXPECTED_ACK.replace('BLDG4', 'x\x82y')
  61 + self.assertEqual(self.protocol.transport.write.call_args[0][0],
  62 + '\x0b' + expected_ack.format('AA') + '\x1c\x0d')
4 twistedhl7/ack.py
... ... @@ -1,6 +1,6 @@
1 1 from hl7 import parse
2 2
3   -_ACK_TEMPLATE = 'MSH|^~\\&|{send_app}|{send_fac}|{recv_app}|{recv_fac}|{dttm}||ACK^001|{msgid}|P|{version}\rMSA|{ack_code}|{msgid}'
  3 +_ACK_TEMPLATE = u'MSH|^~\\&|{send_app}|{send_fac}|{recv_app}|{recv_fac}|{dttm}||ACK^001|{msgid}|P|{version}\rMSA|{ack_code}|{msgid}'
4 4
5 5
6 6 def ACK(original_message, ack_code='AA'):
@@ -18,6 +18,8 @@ def ACK(original_message, ack_code='AA'):
18 18 -
19 19
20 20 """
  21 + # hl7.parse requires the message is unicode already or can be easily
  22 + # converted via unicode()
21 23 msh = parse(original_message).segment('MSH')
22 24
23 25 # easy-access function to make sure unicode is always called
31 twistedhl7/mllp.py
@@ -6,6 +6,7 @@ class IHL7Receiver(Interface):
6 6 # set error handling code
7 7 # set system name
8 8 # set 2.9.2.1 validation
  9 +
9 10 def prepareMessage(original):
10 11 # default to not modifying the message
11 12 return original
@@ -13,7 +14,8 @@ def prepareMessage(original):
13 14 def handleMessage(message):
14 15 """Clients should implement ``handleMessage``, which takes a ``message``
15 16 argument, that is an unparsed HL7 message (the MLLP wrapping around the
16   - HL7 message will be removed).
  17 + HL7 message will be removed). The message will be in unicode, using
  18 + the codec from get_codec() to decode the message.
17 19
18 20 The implementation, if non-blocking, may directly return the ack/nack
19 21 message or can return the ack/nack within a
@@ -24,6 +26,13 @@ def handleMessage(message):
24 26 loop from being blocked.
25 27 """
26 28
  29 + def getCodec():
  30 + """Clients should return the codec name, used when decoding into unicode
  31 +
  32 + http://docs.python.org/library/codecs.html#standard-encodings
  33 + """
  34 + return None
  35 +
27 36 class MinimalLowerLayerProtocol(protocol.Protocol):
28 37 """
29 38 Minimal Lower-Layer Protocol (MLLP) takes the form:
@@ -59,6 +68,11 @@ def onSuccess(message):
59 68
60 69 # only pass messages with data
61 70 if len(message) > 0:
  71 + # convert into unicode (ACK's call to hl7.parse will explode if
  72 + # it receives a non-ASCII byte string, so we just convert to
  73 + # unicode here
  74 + message = self.factory.decode(message)
  75 +
62 76 # error callback (defined here, since error depends on
63 77 # current message). rejects the message
64 78 def onError(err):
@@ -72,6 +86,8 @@ def onError(err):
72 86 d.addErrback(onError)
73 87
74 88 def writeMessage(self, message):
  89 + # convert back to a byte string
  90 + message = self.factory.encode(message)
75 91 # wrap message in payload container
76 92 self.transport.write(
77 93 self.start_block + message + self.end_block + self.carriage_return
@@ -82,9 +98,22 @@ class MLLPFactory(protocol.ServerFactory):
82 98
83 99 def __init__(self, receiver):
84 100 self.receiver = receiver
  101 + self.encoding = receiver.getCodec()
85 102 # set server name
86 103
87 104 def handleMessage(self, message):
88 105 # IHL7Receiver allows implementations to return a Deferred or the
89 106 # result, so ensure we return a Deferred here
90 107 return defer.maybeDeferred(self.receiver.handleMessage, message)
  108 +
  109 + def decode(self, value):
  110 + # turn value into unicode using the receiver's declared codec
  111 + if self.encoding and isinstance(value, str):
  112 + return value.decode(self.encoding)
  113 + return unicode(value)
  114 +
  115 + def encode(self, value):
  116 + # turn value into unicode using the receiver's declared codec
  117 + if self.encoding and isinstance(value, unicode):
  118 + return value.encode(self.encoding)
  119 + return unicode(value)

No commit comments for this range

Something went wrong with that request. Please try again.