Gnumed.business.gmHL7

Package Gnumed :: Package business :: Module gmHL7 |
[frames] | no frames]

# Source Code for Module Gnumed.business.gmHL7

      1  # -*- coding: utf8 -*-
      2  """Some HL7 handling."""
      3  #============================================================
      4  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
      5  __license__ = "GPL v2 or later"
      6
      7
      8  import sys
      9  import os
     10  import codecs
     11  import logging
     12  import time
     13  import datetime as pyDT
     14  import hl7 as pyhl7
     15  from xml.etree import ElementTree as pyxml
     16
     17
     18  if __name__ == '__main__':
     19          sys.path.insert(0, '../../')
     20  from Gnumed.pycommon import gmTools
     21  from Gnumed.pycommon import gmBusinessDBObject
     22  from Gnumed.pycommon import gmPG2
     23  from Gnumed.pycommon import gmDateTime
     24  from Gnumed.business import gmPathLab
     25
     26
     27  _log = logging.getLogger('gm.hl7')
     28
     29  # constants
     30  HL7_EOL = u'\r'
     31
     32  HL7_SEGMENTS = u'FHS BHS MSH PID PV1 OBX NTE ORC OBR'.split()
     33
     34  MSH_sending_lab = 3
     35
     36  PID_name = 5
     37  PID_lastname = 0
     38  PID_firstname = 1
     39  PID_middlename = 2
     40  PID_dob = 7
     41  PID_gender = 8
     42
     43  OBX_type = 3
     44  OBX_LOINC = 0
     45  OBX_name = 1
     46  OBX_value = 5
     47  OBX_unit = 6
     48
     49  HL7_field_labels = {
     50          'PID': {
     51                  0: 'Segment Type',
     52                  1: '<PID> Set ID',
     53                  2: 'Patient ID',
     54                  5: 'Patient name',
     55                  7: 'Date/Time of birth',
     56                  8: 'Administrative gender'
     57          },
     58          'OBR': {
     59                  0: 'Segment Type'
     60          },
     61          'OBX': {
     62                  0: 'Segment Type',
     63                  1: 'Set ID',
     64                  2: 'Value Type',
     65                  3: 'Identifier (LOINC)',
     66                  4: 'Observation Sub-ID',
     67                  5: 'Value',
     68                  6: 'Units',
     69                  7: 'References Range (Low - High)',
     70                  8: 'Abnormal Flags',
     71                  11: 'Result Status',
     72                  14: 'Date/Time of Observation'
     73          }
     74  }
     75
     76  #============================================================
     77  # class to handle unmatched incoming clinical data
     78  #------------------------------------------------------------
     79  _SQL_get_incoming_data = u"""SELECT * FROM clin.v_incoming_data_unmatched WHERE %s"""
     80
     81 -class cIncomingData(gmBusinessDBObject.cBusinessDBObject):
     82          """Represents items of incoming data, say, HL7 snippets."""
     83
     84          _cmd_fetch_payload = _SQL_get_incoming_data % u"pk_incoming_data_unmatched = %s"
     85          _cmds_store_payload = [
     86                  u"""UPDATE clin.incoming_data_unmatched SET
     87                                  fk_patient_candidates = %(pk_patient_candidates)s,
     88                                  fk_identity_disambiguated = %(pk_identity_disambiguated)s,
     89                                  fk_provider_disambiguated = %(pk_provider_disambiguated)s,
     90                                  request_id = gm.nullify_empty_string(%(request_id)s),
     91                                  firstnames = gm.nullify_empty_string(%(firstnames)s),
     92                                  lastnames = gm.nullify_empty_string(%(lastnames)s),
     93                                  dob = %(dob)s,
     94                                  postcode = gm.nullify_empty_string(%(postcode)s),
     95                                  other_info = gm.nullify_empty_string(%(other_info)s),
     96                                  type = gm.nullify_empty_string(%(data_type)s),
     97                                  gender = gm.nullify_empty_string(%(gender)s),
     98                                  requestor = gm.nullify_empty_string(%(requestor)s),
     99                                  external_data_id = gm.nullify_empty_string(%(external_data_id)s),
    100                                  comment = gm.nullify_empty_string(%(comment)s)
    101                          WHERE
    102                                  pk = %(pk_incoming_data_unmatched)s
    103                                          AND
    104                                  xmin = %(xmin_incoming_data_unmatched)s
    105                          RETURNING
    106                                  xmin as xmin_incoming_data_unmatched,
    107                                  octet_length(data) as data_size
    108                  """
    109          ]
    110          # view columns that can be updated:
    111          _updatable_fields = [
    112                  u'pk_patient_candidates',
    113                  u'request_id',                                          # request ID as found in <data>
    114                  u'firstnames',
    115                  u'lastnames',
    116                  u'dob',
    117                  u'postcode',
    118                  u'other_info',                                          # other identifying info in .data
    119                  u'data_type',
    120                  u'gender',
    121                  u'requestor',                                           # Requestor of data (e.g. who ordered test results) if available in source data.
    122                  u'external_data_id',                            # ID of content of .data in external system (e.g. importer) where appropriate
    123                  u'comment',                                                     # a free text comment on this row, eg. why is it here, error logs etc
    124                  u'pk_identity_disambiguated',
    125                  u'pk_provider_disambiguated'            # The provider the data is relevant to.
    126          ]
    127          #--------------------------------------------------------
    128 -        def format(self):
    129                  return u'%s' % self
    130          #--------------------------------------------------------
    131 -        def update_data_from_file(self, fname=None):
    132                  # sanity check
    133                  if not (os.access(fname, os.R_OK) and os.path.isfile(fname)):
    134                          _log.error('[%s] is not a readable file' % fname)
    135                          return False
    136
    137                  gmPG2.file2bytea (
    138                          query = u"UPDATE clin.incoming_data_unmatched SET data = %(data)s::bytea WHERE pk = %(pk)s",
    139                          filename = fname,
    140                          args = {'pk': self.pk_obj}
    141                  )
    142
    143                  # must update XMIN now ...
    144                  self.refetch_payload()
    145                  return True
    146          #--------------------------------------------------------
    147 -        def export_to_file(self, aChunkSize=0, filename=None):
    148
    149                  if self._payload[self._idx['data_size']] == 0:
    150                          return None
    151
    152                  if self._payload[self._idx['data_size']] is None:
    153                          return None
    154
    155                  if filename is None:
    156                          filename = gmTools.get_unique_filename(prefix = 'gm-incoming_data_unmatched-')
    157
    158                  success = gmPG2.bytea2file (
    159                          data_query = {
    160                                  'cmd': u'SELECT substring(data from %(start)s for %(size)s) FROM clin.incoming_data_unmatched WHERE pk = %(pk)s',
    161                                  'args': {'pk': self.pk_obj}
    162                          },
    163                          filename = filename,
    164                          chunk_size = aChunkSize,
    165                          data_size = self._payload[self._idx['data_size']]
    166                  )
    167
    168                  if not success:
    169                          return None
    170
    171                  return filename
    172
    173  #------------------------------------------------------------
    174 -def get_incoming_data(order_by=None):
    175          if order_by is None:
    176                  order_by = u'true'
    177          else:
    178                  order_by = u'true ORDER BY %s' % order_by
    179          cmd = _SQL_get_incoming_data % order_by
    180          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
    181          return [ cIncomingData(row = {'data': r, 'idx': idx, 'pk_field': 'pk_incoming_data_unmatched'}) for r in rows ]
    182
    183  #------------------------------------------------------------
    184 -def create_incoming_data(data_type, filename):
    185          args = {'typ': data_type}
    186          cmd = u"""
    187                  INSERT INTO clin.incoming_data_unmatched (type, data)
    188                  VALUES (%(typ)s, 'new data'::bytea)
    189                  RETURNING pk"""
    190          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
    191          pk = rows[0]['pk']
    192          incoming = cIncomingData(aPK_obj = pk)
    193          if not incoming.update_data_from_file(fname = filename):
    194                  delete_incoming_data(incoming_data = pk)
    195                  return None
    196          return incoming
    197
    198  #------------------------------------------------------------
    199 -def delete_incoming_data(pk_incoming_data=None):
    200          args = {'pk': pk_incoming_data}
    201          cmd = u"DELETE FROM clin.incoming_data_unmatched WHERE pk = %(pk)s"
    202          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    203          return True
    204
    205  #------------------------------------------------------------
    206
    207  #============================================================
    208 -def fix_HL7_stupidities(filename, encoding='utf8'):
    209
    210          out_fname = gmTools.get_unique_filename (
    211                  prefix = u'%s-fixed-' % gmTools.fname_stem(filename),
    212                  suffix = '.hl7'
    213          )
    214          _log.debug('fixing HL7 [%s] -> [%s]', filename, out_fname)
    215          hl7_in = codecs.open(filename, 'rb', encoding)
    216          hl7_out = codecs.open(out_fname, 'wb', 'utf8')
    217
    218          line_idx = 0
    219          prev_line = None
    220          for line in hl7_in:
    221                  line_idx += 1
    222                  # suspicious for non-terminating line ?
    223                  if line.endswith(u' \n'):
    224                          _log.debug('#%s: suspicious non-terminating line ("...<SPACE>\\n"): [%s...%s]', line_idx, line[:4], line[-7:])
    225                          if prev_line is None:
    226                                  prev_line = line[:-1]
    227                          else:
    228                                  prev_line = prev_line + line[:-1]
    229                          continue
    230
    231                  line = line.strip('\r').strip('\n').strip('\r').strip('\n')
    232
    233                  # final continuation line ?
    234                  if line[3] != u'|':
    235                          if prev_line is None:
    236                                  raise ValueError('line #%s does not start with "<SEGMENT>|" but previous line did not end with BLANK either: [%s]' % (line_idx, line))
    237                          hl7_out.write(prev_line)
    238                          prev_line = None
    239                          hl7_out.write(line + HL7_EOL)
    240                          continue
    241
    242                  # start of a known segment ?
    243                  if line[:3] in HL7_SEGMENTS:
    244                          if prev_line is not None:
    245                                  hl7_out.write(prev_line + HL7_EOL)
    246                                  prev_line = None
    247                          hl7_out.write(line + HL7_EOL)
    248                          continue
    249
    250          hl7_out.close()
    251          hl7_in.close()
    252
    253          return out_fname
    254  #============================================================
    255 -def extract_HL7_from_CDATA(filename, xml_path):
    256
    257          _log.debug('extracting HL7 from CDATA of <%s> nodes in XML file [%s]', xml_path, filename)
    258
    259          hl7_xml = pyxml.ElementTree()
    260          try:
    261                  hl7_xml.parse(filename)
    262          except pyxml.ParseError:
    263                  _log.exception('cannot parse [%s]' % filename)
    264                  return None
    265          nodes = hl7_xml.findall(xml_path)
    266          if len(nodes) == 0:
    267                  _log.debug('no data found')
    268                  return None
    269
    270          out_fname = gmTools.get_unique_filename(prefix = u'%s-' % gmTools.fname_stem(filename), suffix = '.hl7')
    271          _log.debug('writing HL7 to [%s]', out_fname)
    272          hl7_file = codecs.open(out_fname, 'wb', 'utf8')
    273          for node in nodes:
    274                  hl7_file.write(node.text)
    275
    276          return out_fname
    277  #============================================================
    278 -def split_HL7_by_MSH(filename, encoding='utf8'):
    279
    280          _log.debug('splitting [%s]', filename)
    281
    282          hl7_in = codecs.open(filename, 'rb', encoding)
    283
    284          idx = 0
    285          first_line = True
    286          MSH_file = None
    287          MSH_fnames = []
    288          for line in hl7_in:
    289                  # first line must be MSH
    290                  if first_line:
    291                          # ignore empty / FHS / BHS lines
    292                          if line.strip() == u'':
    293                                  continue
    294                          if line.startswith(u'FHS|'):
    295                                  _log.debug('ignoring FHS')
    296                                  continue
    297                          if line.startswith(u'BHS|'):
    298                                  _log.debug('ignoring BHS')
    299                                  continue
    300                          if not line.startswith(u'MSH|'):
    301                                  raise ValueError('HL7 file <%s> does not start with "MSH" line' % filename)
    302                          first_line = False
    303                  # start new file
    304                  if line.startswith(u'MSH|'):
    305                          if MSH_file is not None:
    306                                  MSH_file.close()
    307                          idx += 1
    308                          out_fname = gmTools.get_unique_filename(prefix = u'%s-MSH_%s-' % (gmTools.fname_stem(filename), idx), suffix = 'hl7')
    309                          _log.debug('writing message %s to [%s]', idx, out_fname)
    310                          MSH_fnames.append(out_fname)
    311                          MSH_file = codecs.open(out_fname, 'wb', 'utf8')
    312                  # ignore BTS / FTS lines
    313                  if line.startswith(u'BTS|'):
    314                          _log.debug('ignoring BTS')
    315                          continue
    316                  if line.startswith(u'FTS|'):
    317                          _log.debug('ignoring FTS')
    318                          continue
    319                  # else write line to new file
    320                  MSH_file.write(line.strip('\n').strip('\r').strip('\n').strip('\r') + u'\r')
    321
    322          if MSH_file is not None:
    323                  MSH_file.close()
    324          hl7_in.close()
    325
    326          return MSH_fnames
    327
    328  #============================================================
    329 -def flatten_MSH_by_PID(filename):
    330          """Assumes:
    331                  - ONE MSH per file
    332                  - utf8 encoding
    333                  - first non-empty line must be MSH line
    334
    335                  - anything between MSH and PID is lost
    336
    337                  IOW, what's created by split_HL7_into_MSH()
    338          """
    339          _log.debug('splitting [%s]', filename)
    340
    341          MSH_in = codecs.open(filename, 'rb', 'utf8')
    342
    343          looking_for_MSH = True
    344          MSH_line = None
    345          looking_for_first_PID = True
    346          PID_file = None
    347          PID_fnames = []
    348          idx = 0
    349          for line in MSH_in:
    350                  # ignore empty
    351                  if line.strip() == u'':
    352                          continue
    353
    354                  # first non-empty line must be MSH
    355                  if looking_for_MSH:
    356                          if line.startswith(u'MSH|'):
    357                                  looking_for_MSH = False
    358                                  MSH_line = line.strip('\n').strip('\r').strip('\n').strip('\r') + u'\r'
    359                                  continue
    360                          raise ValueError('HL7 MSH file <%s> does not start with "MSH" line' % filename)
    361
    362                  # first non-empty line after MSH must be PID
    363                  if looking_for_first_PID:
    364                          if not line.startswith(u'PID|'):
    365                                  raise ValueError('HL7 MSH file <%s> does not have "PID" line follow "MSH" line' % filename)
    366                          looking_for_first_PID = False
    367
    368                  # start new file if line is PID
    369                  if line.startswith(u'PID|'):
    370                          if PID_file is not None:
    371                                  PID_file.close()
    372                          idx += 1
    373                          out_fname = gmTools.get_unique_filename(prefix = u'%s-PID_%s-' % (gmTools.fname_stem(filename), idx), suffix = 'hl7')
    374                          _log.debug('writing message for PID %s to [%s]', idx, out_fname)
    375                          PID_fnames.append(out_fname)
    376                          PID_file = codecs.open(out_fname, 'wb', 'utf8')
    377                          PID_file.write(MSH_line)
    378                  # else write line to new file
    379                  PID_file.write(line.strip('\n').strip('\r').strip('\n').strip('\r') + u'\r')
    380
    381          if PID_file is not None:
    382                  PID_file.close()
    383          MSH_in.close()
    384
    385          return PID_fnames
    386
    387  #============================================================
    388 -def split_HL7_by_PID(filename, encoding='utf8'):
    389
    390          PID_fnames = []
    391          for MSH_fname in split_HL7_by_MSH(filename, encoding):
    392                  PID_fnames.extend(flatten_MSH_by_PID(MSH_fname))
    393
    394          return PID_fnames
    395
    396  #============================================================
    397 -def __find_or_create_lab(hl7_lab):
    398          comment_tag = u'[HL7 name::%s]' % hl7_lab
    399          for gm_lab in gmPathLab.get_test_orgs():
    400                  if comment_tag in gmTools.coalesce(gm_lab['comment'], u''):
    401                          return gm_lab
    402          _log.debug('lab not found: %s', hl7_lab)
    403          gm_lab = gmPathLab.create_test_org(name = hl7_lab, comment = comment_tag)
    404          if gm_lab is None:
    405                  raise ValueError('cannot create lab [%s] in GNUmed' % hl7_lab)
    406          _log.debug('created lab: %s', gm_lab)
    407          return gm_lab
    408
    409  #------------------------------------------------------------
    410 -def __find_or_create_test_type(loinc, name, pk_lab, unit):
    411
    412          tt = gmPathLab.find_measurement_type(lab = pk_lab, name = name)
    413          if tt is None:
    414                  _log.debug('test type [%s %s (%s)] not found for lab #%s, creating', name, unit, loinc, pk_lab)
    415                  tt = gmPathLab.create_measurement_type(lab = pk_lab, abbrev = name, unit = unit, name = name)
    416
    417          if loinc is None:
    418                  return tt
    419          if loinc.strip() == u'':
    420                  return tt
    421          if tt['loinc'] is None:
    422                  tt['loinc'] = loinc
    423                  tt.save()
    424                  return tt
    425          if tt['loinc'] != loinc:
    426  #               raise ValueError('LOINC code mismatch between GM (%s) and HL7 (%s) for result type [%s]' % (tt['loinc'], loinc, name))
    427                  _log.error('LOINC code mismatch between GM (%s) and HL7 (%s) for result type [%s]', tt['loinc'], loinc, name)
    428
    429          return tt
    430
    431  #------------------------------------------------------------
    432 -def import_MSH(filename):
    433          """Assumes what's produced by flatten_MSH_by_PID()."""
    434
    435          _log.debug('importing HL7 from [%s]', filename)
    436
    437          # read the file
    438          MSH_file = codecs.open(filename, 'rb', 'utf8')
    439          HL7 = pyhl7.parse(MSH_file.read(1024 * 1024 * 5))       # 5 MB max
    440          MSH_file.close()
    441
    442          # verify lab is in database
    443          gm_lab = __find_or_create_lab(HL7.segment('MSH')[MSH_sending_lab][0])
    444
    445          # verify test types are in database
    446          for OBX in HL7.segments('OBX'):
    447                  tt = OBX[OBX_type]
    448                  unit = OBX[OBX_unit][0]
    449                  __find_or_create_test_type(tt[OBX_LOINC], tt[OBX_name], gm_lab['pk_test_org'], unit)
    450
    451          # find patient
    452          name = HL7.segment('PID')[PID_name]
    453          pat_lname = name[PID_lastname]
    454          pat_fname = name[PID_firstname]
    455          pat_mname = None
    456          if len(name) > 2:
    457                  pat_mname = name[PID_middlename]
    458          print " Patient: %s (%s) %s" % (pat_fname, pat_mname, pat_lname)
    459
    460  #------------------------------------------------------------
    461 -def stage_MSH_as_incoming_data(filename, source=None):
    462          """Assumes what's produced by flatten_MSH_by_PID()."""
    463
    464          _log.debug('staging HL7%s from [%s]', gmTools.coalesce(source, u'', u' (%s)'), filename)
    465
    466          # parse HL7
    467          MSH_file = codecs.open(filename, 'rb', 'utf8')
    468          HL7 = pyhl7.parse(MSH_file.read(1024 * 1024 * 5))       # 5 MB max
    469          MSH_file.close()
    470
    471          # import file
    472          inc = create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
    473          if inc is None:
    474                  return None
    475
    476          try:
    477                  # set fields if known
    478                  PID = HL7.segment('PID')
    479                  name = PID[PID_name]
    480                  inc['lastnames'] = gmTools.coalesce(name[PID_lastname], u'')
    481                  inc['firstnames'] = gmTools.coalesce(name[PID_firstname], u'')
    482                  if len(name) > 2:
    483                          inc['firstnames'] += u' '
    484                          inc['firstnames'] += name[PID_middlename]
    485                  if PID[PID_dob] is not None:
    486                          tmp = time.strptime(PID[PID_dob][0], '%Y%m%d')
    487                          inc['dob'] = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
    488                  if PID[PID_gender] is not None:
    489                          inc['gender'] = PID[PID_gender][0]
    490                  inc['external_data_id'] = filename
    491                  #u'fk_patient_candidates',
    492                  #       u'request_id',                                          # request ID as found in <data>
    493                  #       u'postcode',
    494                  #       u'other_info',                                          # other identifying info in .data
    495                  #       u'requestor',                                           # Requestor of data (e.g. who ordered test results) if available in source data.
    496                  #       u'fk_identity_disambiguated',
    497                  #       u'comment',                                                     # a free text comment on this row, eg. why is it here, error logs etc
    498                  #       u'fk_provider_disambiguated'            # The provider the data is relevant to.
    499                  inc.save()
    500          except:
    501                  delete_incoming_data(pk_incoming_data = inc['pk_incoming_data_unmatched'])
    502                  raise
    503
    504          return inc
    505
    506  #------------------------------------------------------------
    507 -def format_hl7_message(message=None, skip_empty_fields=True, eol=u'\n '):
    508          # a segment is a line starting with a type
    509
    510          msg = pyhl7.parse(message)
    511
    512          output = [[_('HL7 Message'), _(' %s segments (lines)%s') % (len(msg), gmTools.bool2subst(skip_empty_fields, _(', skipping empty fields'), u''))]]
    513
    514          max_len = 0
    515          for s_idx in range(len(msg)):
    516                  seg = msg[s_idx]
    517                  seg_type = seg[0][0]
    518
    519                  output.append([_('Segment #%s <%s>') % (s_idx, seg_type), _('%s fields') % len(seg)])
    520
    521                  for f_idx in range(len(seg)):
    522                          field = seg[f_idx]
    523                          try:
    524                                  label = HL7_field_labels[seg_type][f_idx]
    525                          except KeyError:
    526                                  label = _('HL7 %s field') % seg_type
    527
    528                          max_len = max(max_len, len(label))
    529
    530                          if len(field) == 0:
    531                                  if not skip_empty_fields:
    532                                          output.append([u'%2s - %s' % (f_idx, label), _('<EMTPY>')])
    533                                  continue
    534                          if (len(field) == 1) and (field[0].strip() == u''):
    535                                  if not skip_empty_fields:
    536                                          output.append([u'%2s - %s' % (f_idx, label), _('<EMTPY>')])
    537                                  continue
    538
    539                          output.append([u'%2s - %s' % (f_idx, label), u'%s' % field])
    540
    541          if eol is None:
    542                  return output
    543
    544          max_len += 7
    545          return eol.join([ u'%s: %s' % ((o[0] + (u' ' * max_len))[:max_len], o[1]) for o in output ])
    546
    547  #------------------------------------------------------------
    548 -def format_hl7_file(filename, skip_empty_fields=True, eol=u'\n ', return_filename=False):
    549          hl7_file = codecs.open(filename, 'rb', 'utf8')
    550          output = format_hl7_message (
    551                  message = hl7_file.read(1024 * 1024 * 5),               # 5 MB max
    552                  skip_empty_fields = skip_empty_fields,
    553                  eol = eol
    554          )
    555          hl7_file.close()
    556
    557          if not return_filename:
    558                  return output
    559
    560          if eol is None:
    561                  output = u'\n '.join([ u'%s: %s' % ((o[0] + (u' ' * max_len))[:max_len], o[1]) for o in output ])
    562
    563          out_name = gmTools.get_unique_filename(prefix = 'gm-formatted_hl7-', suffix = u'.hl7')
    564          out_file = codecs.open(out_name, 'wb', 'utf8')
    565          out_file.write(output)
    566          out_file.close()
    567
    568          return out_name
    569  #============================================================
    570  # main
    571  #------------------------------------------------------------
    572  if __name__ == "__main__":
    573
    574          if len(sys.argv) < 2:
    575                  sys.exit()
    576
    577          if sys.argv[1] != 'test':
    578                  sys.exit()
    579
    580          from Gnumed.pycommon import gmLog2
    581          from Gnumed.pycommon import gmI18N
    582
    583          gmDateTime.init()
    584          gmI18N.activate_locale()
    585          gmI18N.install_domain()
    586
    587          #-------------------------------------------------------
    588 -        def test_import_HL7():
    589                  PID_names = split_HL7_by_PID(sys.argv[2], encoding='utf8')
    590                  for name in PID_names:
    591                          print name
    592                          import_MSH(name)
    593          #-------------------------------------------------------
    594 -        def test_xml_extract():
    595                  hl7 = extract_HL7_from_CDATA(sys.argv[2], u'.//Message')
    596                  print "HL7:", hl7
    597                  fixed = fix_HL7_stupidities(hl7)
    598                  print "fixed HL7:", fixed
    599                  PID_names = split_HL7_by_PID(fixed, encoding='utf8')
    600                  print "per-PID MSH files:"
    601                  for name in PID_names:
    602                          print " ", name
    603          #-------------------------------------------------------
    604 -        def test_incoming_data():
    605                  for d in get_incoming_data():
    606                          print d
    607          #-------------------------------------------------------
    608 -        def test_stage_hl7_from_xml():
    609                  hl7 = extract_HL7_from_CDATA(sys.argv[2], u'.//Message')
    610                  print "HL7:", hl7
    611                  fixed = fix_HL7_stupidities(hl7)
    612                  print "fixed HL7:", fixed
    613                  PID_names = split_HL7_by_PID(fixed, encoding='utf8')
    614                  print "staging per-PID HL7 files:"
    615                  for name in PID_names:
    616                          print " file:", name
    617                          print "", stage_MSH_as_incoming_data(name, source = u'Excelleris')
    618          #-------------------------------------------------------
    619 -        def test_stage_hl7():
    620                  fixed = fix_HL7_stupidities(sys.argv[2])
    621                  print "fixed HL7:", fixed
    622                  PID_names = split_HL7_by_PID(fixed, encoding='utf8')
    623                  print "staging per-PID HL7 files:"
    624                  for name in PID_names:
    625                          print " file:", name
    626                          print "", stage_MSH_as_incoming_data(name, source = u'?')
    627          #-------------------------------------------------------
    628 -        def test_format_hl7_message():
    629                  tests = [
    630                          "OBR|1||03-1350023-LIP-0|LIP^Lipids||20031004073300|20031004073300|||||||20031004073300||22333^MEDIC^IAN^TEST||031350023||03-1350023|031350023|20031004131600||CHEM|F|||22333^MEDIC^IAN^TEST",
    631                          "OBX|2|NM|22748-8^LDL Cholesterol||4.0|mmol/L|1.5 - 3.4|H|||F|||20031004073300"
    632                  ]
    633                  for test in tests:
    634                          print format_hl7_message (
    635  #                               skip_empty_fields = True,
    636                                  message = test
    637                          )
    638          #-------------------------------------------------------
    639 -        def test_format_hl7_file():
    640                  print format_hl7_file (
    641                          sys.argv[2]
    642  #                       skip_empty_fields = True
    643                  )
    644          #-------------------------------------------------------
    645          #test_import_HL7()
    646          #test_xml_extract()
    647          #test_incoming_data()
    648          #test_stage_hl7_from_xml()
    649          #test_stage_hl7()
    650          #test_format_hl7_message()
    651          test_format_hl7_file()
    652