Package Gnumed :: Package wxpython :: Module gmDataMiningWidgets |
[frames] | no frames]

# Source Code for Module Gnumed.wxpython.gmDataMiningWidgets

      1  """GNUmed data mining related widgets."""
      2
      3  #================================================================
      4  __author__ = 'karsten.hilbert@gmx.net'
      5  __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
      6
      7
      8  # stdlib
      9  import sys
     10  import os
     11  import fileinput
     12  import logging
     13  import io
     14  import csv
     15
     16
     17  # 3rd party
     18  import wx
     19
     20
     21  # GNUmed
     22  if __name__ == '__main__':
     23          sys.path.insert(0, '../../')
     24  from Gnumed.pycommon import gmDispatcher
     25  from Gnumed.pycommon import gmMimeLib
     26  from Gnumed.pycommon import gmTools
     27  from Gnumed.pycommon import gmPG2
     28  from Gnumed.pycommon import gmMatchProvider
     29  from Gnumed.pycommon import gmI18N
     30  from Gnumed.pycommon import gmNetworkTools
     31  from Gnumed.pycommon.gmExceptions import ConstructorError
     32
     33  from Gnumed.business import gmPerson
     34  from Gnumed.business import gmDataMining
     35  from Gnumed.business import gmPersonSearch
     36
     37  from Gnumed.wxpython import gmGuiHelpers
     38  from Gnumed.wxpython import gmListWidgets
     39
     40
     41  _log = logging.getLogger('gm.ui')
     42  #================================================================
     43 -class cPatientListingCtrl(gmListWidgets.cReportListCtrl):
     44
     45 -        def __init__(self, *args, **kwargs):
     46                  """<patient_key> must index or name a column in self.__data"""
     47                  try:
     48                          self.patient_key = kwargs['patient_key']
     49                          del kwargs['patient_key']
     50                  except KeyError:
     51                          self.patient_key = None
     52
     53                  gmListWidgets.cReportListCtrl.__init__(self, *args, **kwargs)
     54
     55                  self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._on_list_item_activated, self)
     56          #------------------------------------------------------------
     57 -        def __get_patient_pk_data_key(self, data=None):
     58                  if self.data is None:
     59                          return None
     60
     61                  if len(self.data) == 0:
     62                          return None
     63
     64                  if data is None:
     65                          data = self.get_selected_item_data(only_one = True)
     66
     67                  if data is None:
     68                          data = self.get_item_data(item_idx = 0)
     69
     70                  if data is None:
     71                          return None
     72
     73                  if self.patient_key is not None:
     74                          try:
     75                                  data[self.patient_key]
     76                                  return self.patient_key
     77                          except (KeyError, IndexError, TypeError):
     78                                  # programming error
     79                                  _log.error('misconfigured identifier column <%s>', self.patient_key)
     80
     81                  _log.debug('identifier column not configured, trying to detect')
     82
     83                  if 'pk_patient' in data:
     84                          return 'pk_patient'
     85
     86                  if 'fk_patient' in data:
     87                          return 'fk_patient'
     88
     89                  if 'pk_identity' in data:
     90                          return 'pk_identity'
     91
     92                  if 'fk_identity' in data:
     93                          return 'fk_identity'
     94
     95                  if 'id_identity' in data:
     96                          return 'id_identity'
     97
     98                  return gmListWidgets.get_choices_from_list (
     99                          parent = self,
    100                          msg = _(
    101                                  'The report result list does not contain any of the following columns:\n'
    102                                  '\n'
    103                                  ' <%s> / pk_patient / fk_patient\n'
    104                                  ' pk_identity / fk_identity / id_identity\n'
    105                                  '\n'
    106                                  'Select the column which contains patient IDs:\n'
    107                          ) % self.patient_key,
    108                          caption = _('Choose column from query results ...'),
    109                          choices = data.keys(),
    110                          columns = [_('Column name')],
    111                          single_selection = True
    112                  )
    113
    114          patient_pk_data_key = property(__get_patient_pk_data_key, lambda x:x)
    115          #------------------------------------------------------------
    116          # event handling
    117          #------------------------------------------------------------
    118 -        def _on_list_item_activated(self, evt):
    119                  data = self.get_selected_item_data(only_one = True)
    120                  pk_pat_col = self.__get_patient_pk_data_key(data = data)
    121
    122                  if pk_pat_col is None:
    123                          gmDispatcher.send(signal = 'statustext', msg = _('List not known to be patient-related.'))
    124                          return
    125
    126                  pat_data = data[pk_pat_col]
    127                  try:
    128                          pat_pk = int(pat_data)
    129                          pat = gmPerson.cPerson(aPK_obj = pat_pk)
    130                  except (ValueError, TypeError):
    131                          searcher = gmPersonSearch.cPatientSearcher_SQL()
    132                          idents = searcher.get_identities(pat_data)
    133                          if len(idents) == 0:
    134                                  gmDispatcher.send(signal = 'statustext', msg = _('No matching patient found.'))
    135                                  return
    136                          if len(idents) == 1:
    137                                  pat = idents[0]
    138                          else:
    139                                  from Gnumed.wxpython import gmPatSearchWidgets
    140                                  dlg = gmPatSearchWidgets.cSelectPersonFromListDlg(parent=wx.GetTopLevelParent(self), id=-1)
    141                                  dlg.set_persons(persons=idents)
    142                                  result = dlg.ShowModal()
    143                                  if result == wx.ID_CANCEL:
    144                                          dlg.DestroyLater()
    145                                          return
    146                                  pat = dlg.get_selected_person()
    147                                  dlg.DestroyLater()
    148                  except ConstructorError:
    149                          gmDispatcher.send(signal = 'statustext', msg = _('No matching patient found.'))
    150                          return
    151
    152                  from Gnumed.wxpython import gmPatSearchWidgets
    153                  gmPatSearchWidgets.set_active_patient(patient = pat)
    154
    155  #================================================================
    156  from Gnumed.wxGladeWidgets import wxgPatientListingPnl
    157
    158 -class cPatientListingPnl(wxgPatientListingPnl.wxgPatientListingPnl):
    159
    160 -        def __init__(self, *args, **kwargs):
    161
    162                  try:
    163                          button_defs = kwargs['button_defs'][:5]
    164                          del kwargs['button_defs']
    165                  except KeyError:
    166                          button_defs = []
    167
    168                  try:
    169                          msg = kwargs['message']
    170                          del kwargs['message']
    171                  except KeyError:
    172                          msg = None
    173
    174                  wxgPatientListingPnl.wxgPatientListingPnl.__init__(self, *args, **kwargs)
    175
    176                  if msg is not None:
    177                          self._lbl_msg.SetLabel(msg)
    178
    179                  buttons = [self._BTN_1, self._BTN_2, self._BTN_3, self._BTN_4, self._BTN_5]
    180                  for idx in range(len(button_defs)):
    181                          button_def = button_defs[idx]
    182                          if button_def['label'].strip() == '':
    183                                  continue
    184                          buttons[idx].SetLabel(button_def['label'])
    185                          buttons[idx].SetToolTip(button_def['tooltip'])
    186                          buttons[idx].Enable(True)
    187
    188                  self.Fit()
    189          #------------------------------------------------------------
    190          # event handling
    191          #------------------------------------------------------------
    192 -        def _on_BTN_1_pressed(self, event):
    193                  event.Skip()
    194          #------------------------------------------------------------
    195 -        def _on_BTN_2_pressed(self, event):
    196                  event.Skip()
    197          #------------------------------------------------------------
    198 -        def _on_BTN_3_pressed(self, event):
    199                  event.Skip()
    200          #------------------------------------------------------------
    201 -        def _on_BTN_4_pressed(self, event):
    202                  event.Skip()
    203          #------------------------------------------------------------
    204 -        def _on_BTN_5_pressed(self, event):
    205                  event.Skip()
    206
    207  #================================================================
    208  from Gnumed.wxGladeWidgets import wxgDataMiningPnl
    209
    210 -class cDataMiningPnl(wxgDataMiningPnl.wxgDataMiningPnl):
    211
    212 -        def __init__(self, *args, **kwargs):
    213                  wxgDataMiningPnl.wxgDataMiningPnl.__init__(self, *args, **kwargs)
    214
    215                  self.__init_ui()
    216
    217                  # make me a file drop target
    218                  dt = gmGuiHelpers.cFileDropTarget(target = self)
    219                  self.SetDropTarget(dt)
    220          #--------------------------------------------------------
    221 -        def __init_ui(self):
    222                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    223                          queries = ["""
    224                                  SELECT DISTINCT ON (label)
    225                                          cmd,
    226                                          label
    227                                  FROM cfg.report_query
    228                                  WHERE
    229                                          label %(fragment_condition)s
    230                                                  OR
    231                                          cmd %(fragment_condition)s
    232                          """]
    233                  )
    234                  mp.setThresholds(2,3,5)
    235                  self._PRW_report_name.matcher = mp
    236                  self._PRW_report_name.add_callback_on_selection(callback = self._on_report_selected)
    237                  self._PRW_report_name.add_callback_on_lose_focus(callback = self._auto_load_report)
    238          #--------------------------------------------------------
    239 -        def _auto_load_report(self, *args, **kwargs):
    240                  if self._TCTRL_query.GetValue() == '':
    241                          if self._PRW_report_name.GetData() is not None:
    242                                  self._TCTRL_query.SetValue(self._PRW_report_name.GetData())
    243                                  self._BTN_run.SetFocus()
    244          #--------------------------------------------------------
    245 -        def _on_report_selected(self, *args, **kwargs):
    246                  self._TCTRL_query.SetValue(self._PRW_report_name.GetData())
    247                  self._BTN_run.SetFocus()
    248          #--------------------------------------------------------
    249          # file drop target API
    250          #--------------------------------------------------------
    251 -        def _drop_target_consume_filenames(self, filenames):
    252                  # act on first file only
    253                  fname = filenames[0]
    254                  _log.debug('importing SQL from <%s>', fname)
    255                  # act on text files only
    256                  mime_type = gmMimeLib.guess_mimetype(fname)
    257                  _log.debug('mime type: %s', mime_type)
    258                  if not mime_type.startswith('text/'):
    259                          _log.debug('not a text file')
    260                          gmDispatcher.send(signal='statustext', msg = _('Cannot read SQL from [%s]. Not a text file.') % fname, beep = True)
    261                          return False
    262  #               # act on "small" files only
    263  #               stat_val = os.stat(fname)
    264  #               if stat_val.st_size > 5000:
    265  #                       gmDispatcher.send(signal='statustext', msg = _('Cannot read SQL from [%s]. File too big (> 2000 bytes).') % fname, beep = True)
    266  #                       return False
    267                  # all checks passed
    268                  for line in fileinput.input(fname):
    269                          self._TCTRL_query.AppendText(line)
    270          #--------------------------------------------------------
    271          # notebook plugin API
    272          #--------------------------------------------------------
    273 -        def repopulate_ui(self):
    274                  pass
    275          #--------------------------------------------------------
    276          # event handlers
    277          #--------------------------------------------------------
    278 -        def _on_contribute_button_pressed(self, evt):
    279                  report = self._PRW_report_name.GetValue().strip()
    280                  if report == '':
    281                          gmDispatcher.send(signal = 'statustext', msg = _('Report must have a name for contribution.'), beep = False)
    282                          return
    283
    284                  query = self._TCTRL_query.GetValue().strip()
    285                  if query == '':
    286                          gmDispatcher.send(signal = 'statustext', msg = _('Report must have a query for contribution.'), beep = False)
    287                          return
    288
    289                  do_it = gmGuiHelpers.gm_show_question (
    290                          _(      'Be careful that your contribution (the query itself) does\n'
    291                                  'not contain any person-identifiable search parameters.\n'
    292                                  '\n'
    293                                  'Note, however, that no query result data whatsoever\n'
    294                                  'is included in the contribution that will be sent.\n'
    295                                  '\n'
    296                                  'Are you sure you wish to send this query to\n'
    297                                  'the gnumed community mailing list?\n'
    298                          ),
    299                          _('Contributing custom report')
    300                  )
    301                  if not do_it:
    302                          return
    303
    304                  msg = """--- This is a report definition contributed by a GNUmed user.
    305
    306  --- Save it as a text file and drop it onto the Report Generator
    307  --- inside GNUmed in order to take advantage of the contribution.
    308
    309  ----------------------------------------
    310
    311  --- %s
    312
    313  %s
    314
    315  ----------------------------------------
    316
    317  --- The GNUmed client.
    318  """ % (report, query)
    319
    320                  auth = {'user': gmNetworkTools.default_mail_sender, 'password': 'gnumed-at-gmx-net'}
    321                  if not gmNetworkTools.compose_and_send_email (
    322                          sender = 'GNUmed Report Generator <gnumed@gmx.net>',
    323                          receiver = ['gnumed-devel@gnu.org'],
    324                          subject = 'user contributed report',
    325                          message = msg,
    326                          server = gmNetworkTools.default_mail_server,
    327                          auth = auth
    328                  ):
    329                          gmDispatcher.send(signal = 'statustext', msg = _('Unable to send mail. Cannot contribute report [%s] to GNUmed community.') % report, beep = True)
    330                          return False
    331
    332                  gmDispatcher.send(signal = 'statustext', msg = _('Thank you for your contribution to the GNUmed community!'), beep = False)
    333                  return True
    334          #--------------------------------------------------------
    335 -        def _on_schema_button_pressed(self, evt):
    336                  # will block when called in text mode (that is, from a terminal, too !)
    337                  gmNetworkTools.open_url_in_browser(url = 'http://wiki.gnumed.de/bin/view/Gnumed/DatabaseSchema')
    338          #--------------------------------------------------------
    339 -        def _on_delete_button_pressed(self, evt):
    340                  report = self._PRW_report_name.GetValue().strip()
    341                  if report == '':
    342                          return True
    343                  if gmDataMining.delete_report_definition(name=report):
    344                          self._PRW_report_name.SetText()
    345                          self._TCTRL_query.SetValue('')
    346                          gmDispatcher.send(signal='statustext', msg = _('Deleted report definition [%s].') % report, beep=False)
    347                          return True
    348                  gmDispatcher.send(signal='statustext', msg = _('Error deleting report definition [%s].') % report, beep=True)
    349                  return False
    350          #--------------------------------------------------------
    351 -        def _on_clear_button_pressed(self, evt):
    352                  self._PRW_report_name.SetText()
    353                  self._TCTRL_query.SetValue('')
    354                  self._LCTRL_result.set_columns()
    355          #--------------------------------------------------------
    356 -        def _on_save_button_pressed(self, evt):
    357                  report = self._PRW_report_name.GetValue().strip()
    358                  if report == '':
    359                          gmDispatcher.send(signal='statustext', msg = _('Cannot save report definition without name.'), beep=True)
    360                          return False
    361                  query = self._TCTRL_query.GetValue().strip()
    362                  if query == '':
    363                          gmDispatcher.send(signal='statustext', msg = _('Cannot save report definition without query.'), beep=True)
    364                          return False
    365                  # FIXME: check for exists and ask for permission
    366                  if gmDataMining.save_report_definition(name=report, query=query, overwrite=True):
    367                          gmDispatcher.send(signal='statustext', msg = _('Saved report definition [%s].') % report, beep=False)
    368                          return True
    369                  gmDispatcher.send(signal='statustext', msg = _('Error saving report definition [%s].') % report, beep=True)
    370                  return False
    371          #--------------------------------------------------------
    372 -        def _on_visualize_button_pressed(self, evt):
    373
    374                  try:
    375                          # better fail early
    376                          import Gnuplot
    377                  except ImportError:
    378                          gmGuiHelpers.gm_show_info (
    379                                  aMessage = _('Cannot import "Gnuplot" python module.'),
    380                                  aTitle = _('Query result visualizer')
    381                          )
    382                          return
    383
    384                  x_col = gmListWidgets.get_choices_from_list (
    385                          parent = self,
    386                          msg = _('Choose a column to be used as the X-Axis:'),
    387                          caption = _('Choose column from query results ...'),
    388                          choices = self.query_results[0].keys(),
    389                          columns = [_('Column name')],
    390                          single_selection = True
    391                  )
    392                  if x_col is None:
    393                          return
    394
    395                  y_col = gmListWidgets.get_choices_from_list (
    396                          parent = self,
    397                          msg = _('Choose a column to be used as the Y-Axis:'),
    398                          caption = _('Choose column from query results ...'),
    399                          choices = self.query_results[0].keys(),
    400                          columns = [_('Column name')],
    401                          single_selection = True
    402                  )
    403                  if y_col is None:
    404                          return
    405
    406                  # FIXME: support debugging (debug=1) depending on --debug
    407                  gp = Gnuplot.Gnuplot(persist=1)
    408                  if self._PRW_report_name.GetValue().strip() != '':
    409                          gp.title(_('GNUmed report: %s') % self._PRW_report_name.GetValue().strip()[:40])
    410                  else:
    411                          gp.title(_('GNUmed report results'))
    412                  gp.xlabel(x_col)
    413                  gp.ylabel(y_col)
    414                  try:
    415                          gp.plot([ [r[x_col], r[y_col]] for r in self.query_results ])
    416                  except Exception:
    417                          _log.exception('unable to plot results from [%s:%s]' % (x_col, y_col))
    418                          gmDispatcher.send(signal = 'statustext', msg = _('Error plotting data.'), beep = True)
    419
    420          #--------------------------------------------------------
    421 -        def _on_waiting_list_button_pressed(self, event):
    422                  event.Skip()
    423
    424                  pat_pk_key = self._LCTRL_result.patient_pk_data_key
    425                  if pat_pk_key is None:
    426                          gmGuiHelpers.gm_show_info (
    427                                  info = _('These report results do not seem to contain per-patient data.'),
    428                                  title = _('Using report results')
    429                          )
    430                          return
    431
    432                  zone = wx.GetTextFromUser (
    433                          _('Enter a waiting zone to put patients in:'),
    434                          caption = _('Using report results'),
    435                          default_value = _('search results')
    436                  )
    437                  if zone.strip() == '':
    438                          return
    439
    440                  data = self._LCTRL_result.get_selected_item_data(only_one = False)
    441                  if data is None:
    442                          use_all = gmGuiHelpers.gm_show_question (
    443                                  title = _('Using report results'),
    444                                  question = _('No results selected.\n\nTransfer ALL patients from results to waiting list ?'),
    445                                  cancel_button = True
    446                          )
    447                          if not use_all:
    448                                  return
    449                          data = self._LCTRL_result.data
    450
    451                  comment = self._PRW_report_name.GetValue().strip()
    452                  for item in data:
    453                          pat = gmPerson.cPerson(aPK_obj = item[pat_pk_key])
    454                          pat.put_on_waiting_list (comment = comment, zone = zone)
    455
    456          #--------------------------------------------------------
    457 -        def _on_save_results_button_pressed(self, event):
    458                  event.Skip()
    459
    460                  user_query = self._TCTRL_query.GetValue().strip().strip(';')
    461                  if user_query == '':
    462                          return
    463
    464                  pat = None
    465                  curr_pat = gmPerson.gmCurrentPatient()
    466                  if curr_pat.connected:
    467                          pat = curr_pat.ID
    468                  success, hint, cols, rows = gmDataMining.run_report_query (
    469                          query = user_query,
    470                          limit = None,
    471                          pk_identity = pat
    472                  )
    473
    474                  if not success:
    475                          return
    476
    477                  if len(rows) == 0:
    478                          return
    479
    480                  dlg = wx.FileDialog (
    481                          parent = self,
    482                          message = _("Save SQL report query results as CSV in..."),
    483                          defaultDir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed'))),
    484                          defaultFile = 'gm-query_results.csv',
    485                          wildcard = '%s (*.csv)|*.csv|%s (*)|*' % (_("CSV files"), _("all files")),
    486                          style = wx.FD_SAVE
    487                  )
    488                  choice = dlg.ShowModal()
    489                  csv_name = dlg.GetPath()
    490                  dlg.DestroyLater()
    491                  if choice != wx.ID_OK:
    492                          return
    493
    494                  csv_file = io.open(csv_name, mode = 'wt', encoding = 'utf8')
    495                  csv_file.write('#-------------------------------------------------------------------------------------\n')
    496                  csv_file.write('# GNUmed SQL report results\n')
    497                  csv_file.write('#\n')
    498                  csv_file.write('# Report: "%s"\n' % self._PRW_report_name.GetValue().strip())
    499                  csv_file.write('#\n')
    500                  csv_file.write('# SQL:\n')
    501                  for line in user_query.split('\n'):
    502                          csv_file.write('# %s\n' % line)
    503                  csv_file.write('#\n')
    504                  csv_file.write('# ID of active patient: %s\n' % pat)
    505                  csv_file.write('#\n')
    506                  csv_file.write('# hits found: %s\n' % len(rows))
    507                  csv_file.write('#-------------------------------------------------------------------------------------\n')
    508
    509                  csv_writer = csv.writer(csv_file)
    510                  csv_writer.writerow(cols)
    511                  for row in rows:
    512                          csv_writer.writerow(row)
    513
    514                  csv_file.close()
    515
    516          #--------------------------------------------------------
    517 -        def _on_run_button_pressed(self, evt):
    518
    519                  self._BTN_visualize.Enable(False)
    520                  self._BTN_waiting_list.Enable(False)
    521                  self._BTN_save_results.Enable(False)
    522
    523                  user_query = self._TCTRL_query.GetValue().strip().strip(';')
    524                  if user_query == '':
    525                          return True
    526
    527                  limit = 1001
    528                  pat = None
    529                  curr_pat = gmPerson.gmCurrentPatient()
    530                  if curr_pat.connected:
    531                          pat = curr_pat.ID
    532                  success, hint, cols, rows = gmDataMining.run_report_query (
    533                          query = user_query,
    534                          limit = limit,
    535                          pk_identity = pat
    536                  )
    537
    538                  self._LCTRL_result.set_columns()
    539
    540                  if len(rows) == 0:
    541                          self._LCTRL_result.set_columns([_('Results')])
    542                          self._LCTRL_result.set_string_items([[_('Report returned no data.')]])
    543                          self._LCTRL_result.set_column_widths()
    544                          gmDispatcher.send('statustext', msg = _('No data returned for this report.'), beep = True)
    545                          return True
    546
    547                  gmDispatcher.send(signal = 'statustext', msg = _('Found %s results.') % len(rows))
    548
    549                  if len(rows) == 1001:
    550                          gmGuiHelpers.gm_show_info (
    551                                  aMessage = _(
    552                                          'This query returned at least %s results.\n'
    553                                          '\n'
    554                                          'GNUmed will only show the first %s rows.\n'
    555                                          '\n'
    556                                          'You may want to narrow down the WHERE conditions\n'
    557                                          'or use LIMIT and OFFSET to batchwise go through\n'
    558                                          'all the matching rows.'
    559                                  ) % (limit, limit-1),
    560                                  aTitle = _('Report Generator')
    561                          )
    562                          rows = rows[:-1]                # make it true :-)
    563
    564                  self._LCTRL_result.set_columns(cols)
    565                  for row in rows:
    566                          try:
    567                                  label = str(gmTools.coalesce(row[0], '')).replace('\n', '<LF>').replace('\r', '<CR>')
    568                          except UnicodeDecodeError:
    569                                  label = _('not str()able')
    570                          if len(label) > 150:
    571                                  label = label[:150] + gmTools.u_ellipsis
    572                          row_num = self._LCTRL_result.InsertItem(sys.maxsize, label = label)
    573                          for col_idx in range(1, len(row)):
    574                                  try:
    575                                          label = str(gmTools.coalesce(row[col_idx], '')).replace('\n', '<LF>').replace('\r', '<CR>')[:250]
    576                                  except UnicodeDecodeError:
    577                                          label = _('not str()able')
    578                                  if len(label) > 150:
    579                                          label = label[:150] + gmTools.u_ellipsis
    580                                  self._LCTRL_result.SetItem (
    581                                          index = row_num,
    582                                          column = col_idx,
    583                                          label = label
    584                                  )
    585                  # must be called explicitely, because string items are set above without calling set_string_items
    586                  self._LCTRL_result._invalidate_sorting_metadata()
    587                  self._LCTRL_result.set_column_widths()
    588                  self._LCTRL_result.set_data(data = rows)
    589
    590                  self.query_results = rows
    591                  self._BTN_visualize.Enable(True)
    592                  self._BTN_waiting_list.Enable(True)
    593                  self._BTN_save_results.Enable(True)
    594
    595                  return success
    596
    597  #================================================================
    598  # main
    599  #----------------------------------------------------------------
    600  if __name__ == '__main__':
    601          from Gnumed.pycommon import gmI18N, gmDateTime
    602
    603          gmI18N.activate_locale()
    604          gmI18N.install_domain()
    605          gmDateTime.init()
    606
    607          #------------------------------------------------------------
    608 -        def test_pat_list_ctrl():
    609                  app = wx.PyWidgetTester(size = (400, 500))
    610                  lst = cPatientListingCtrl(app.frame, patient_key = 0)
    611                  lst.set_columns(['name', 'comment'])
    612                  lst.set_string_items([
    613                          ['Kirk', 'Kirk by name'],
    614                          ['#12', 'Kirk by ID'],
    615                          ['unknown', 'unknown patient']
    616                  ])
    617  #               app.SetWidget(cPatientListingCtrl, patient_key = 0)
    618                  app.frame.Show()
    619                  app.MainLoop()
    620          #------------------------------------------------------------
    621
    622          test_pat_list_ctrl()
    623