Gnumed.wxpython.gmDemographicsWidgets

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

# Source Code for Module Gnumed.wxpython.gmDemographicsWidgets

       1  """Widgets dealing with patient demographics."""
       2  #============================================================
       3  __author__ = "R.Terry, SJ Tan, I Haywood, Carlos Moro <cfmoro1976@yahoo.es>"
       4  __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
       5
       6  # standard library
       7  import sys
       8  import sys
       9  import codecs
      10  import re as regex
      11  import logging
      12  import os
      13  import datetime as pydt
      14
      15
      16  import wx
      17  import wx.wizard
      18  import wx.lib.imagebrowser as wx_imagebrowser
      19  import wx.lib.statbmp as wx_genstatbmp
      20
      21
      22  # GNUmed specific
      23  if __name__ == '__main__':
      24          sys.path.insert(0, '../../')
      25  from Gnumed.pycommon import gmDispatcher
      26  from Gnumed.pycommon import gmI18N
      27  from Gnumed.pycommon import gmMatchProvider
      28  from Gnumed.pycommon import gmPG2
      29  from Gnumed.pycommon import gmTools
      30  from Gnumed.pycommon import gmCfg
      31  from Gnumed.pycommon import gmDateTime
      32  from Gnumed.pycommon import gmShellAPI
      33  from Gnumed.pycommon import gmNetworkTools
      34
      35  from Gnumed.business import gmDemographicRecord
      36  from Gnumed.business import gmPersonSearch
      37  from Gnumed.business import gmPerson
      38  from Gnumed.business import gmStaff
      39
      40  from Gnumed.wxpython import gmPhraseWheel
      41  from Gnumed.wxpython import gmRegetMixin
      42  from Gnumed.wxpython import gmAuthWidgets
      43  from Gnumed.wxpython import gmPersonContactWidgets
      44  from Gnumed.wxpython import gmEditArea
      45  from Gnumed.wxpython import gmListWidgets
      46  from Gnumed.wxpython import gmDateTimeInput
      47  from Gnumed.wxpython import gmDataMiningWidgets
      48  from Gnumed.wxpython import gmGuiHelpers
      49
      50
      51  # constant defs
      52  _log = logging.getLogger('gm.ui')
      53
      54
      55  try:
      56          _('dummy-no-need-to-translate-but-make-epydoc-happy')
      57  except NameError:
      58          _ = lambda x:x
      59
      60  #============================================================
      61  # image tags related widgets
      62  #------------------------------------------------------------
      63 -def edit_tag_image(parent=None, tag_image=None, single_entry=False):
      64          if tag_image is not None:
      65                  if tag_image['is_in_use']:
      66                          gmGuiHelpers.gm_show_info (
      67                                  aTitle = _('Editing tag'),
      68                                  aMessage = _(
      69                                          'Cannot edit the image tag\n'
      70                                          '\n'
      71                                          ' "%s"\n'
      72                                          '\n'
      73                                          'because it is currently in use.\n'
      74                                  ) % tag_image['l10n_description']
      75                          )
      76                          return False
      77
      78          ea = cTagImageEAPnl(parent = parent, id = -1)
      79          ea.data = tag_image
      80          ea.mode = gmTools.coalesce(tag_image, 'new', 'edit')
      81          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
      82          dlg.SetTitle(gmTools.coalesce(tag_image, _('Adding new tag'), _('Editing tag')))
      83          if dlg.ShowModal() == wx.ID_OK:
      84                  dlg.Destroy()
      85                  return True
      86          dlg.Destroy()
      87          return False
      88  #------------------------------------------------------------
      89 -def manage_tag_images(parent=None):
      90
      91          if parent is None:
      92                  parent = wx.GetApp().GetTopWindow()
      93          #------------------------------------------------------------
      94          def go_to_openclipart_org(tag_image):
      95                  gmNetworkTools.open_url_in_browser(url = u'http://www.openclipart.org')
      96                  gmNetworkTools.open_url_in_browser(url = u'http://commons.wikimedia.org/wiki/Category:Symbols_of_disabilities')
      97                  gmNetworkTools.open_url_in_browser(url = u'http://www.duckduckgo.com')
      98                  gmNetworkTools.open_url_in_browser(url = u'http://images.google.com')
      99                  return True
     100          #------------------------------------------------------------
     101          def edit(tag_image=None):
     102                  return edit_tag_image(parent = parent, tag_image = tag_image, single_entry = (tag_image is not None))
     103          #------------------------------------------------------------
     104          def delete(tag):
     105                  if tag['is_in_use']:
     106                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this tag. It is in use.'), beep = True)
     107                          return False
     108
     109                  return gmDemographicRecord.delete_tag_image(tag_image = tag['pk_tag_image'])
     110          #------------------------------------------------------------
     111          def refresh(lctrl):
     112                  tags = gmDemographicRecord.get_tag_images(order_by = u'l10n_description')
     113                  items = [ [
     114                          t['l10n_description'],
     115                          gmTools.bool2subst(t['is_in_use'], u'X', u''),
     116                          u'%s' % t['size'],
     117                          t['pk_tag_image']
     118                  ] for t in tags ]
     119                  lctrl.set_string_items(items)
     120                  lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE])
     121                  lctrl.set_data(tags)
     122          #------------------------------------------------------------
     123          msg = _('\nTags with images registered with GNUmed.\n')
     124
     125          tag = gmListWidgets.get_choices_from_list (
     126                  parent = parent,
     127                  msg = msg,
     128                  caption = _('Showing tags with images.'),
     129                  columns = [_('Tag name'), _('In use'), _('Image size'), u'#'],
     130                  single_selection = True,
     131                  new_callback = edit,
     132                  edit_callback = edit,
     133                  delete_callback = delete,
     134                  refresh_callback = refresh,
     135                  left_extra_button = (_('WWW'), _('Go to www.openclipart.org for images.'), go_to_openclipart_org)
     136          )
     137
     138          return tag
     139  #------------------------------------------------------------
     140  from Gnumed.wxGladeWidgets import wxgTagImageEAPnl
     141
     142 -class cTagImageEAPnl(wxgTagImageEAPnl.wxgTagImageEAPnl, gmEditArea.cGenericEditAreaMixin):
     143
     144 -        def __init__(self, *args, **kwargs):
     145
     146                  try:
     147                          data = kwargs['tag_image']
     148                          del kwargs['tag_image']
     149                  except KeyError:
     150                          data = None
     151
     152                  wxgTagImageEAPnl.wxgTagImageEAPnl.__init__(self, *args, **kwargs)
     153                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     154
     155                  self.mode = 'new'
     156                  self.data = data
     157                  if data is not None:
     158                          self.mode = 'edit'
     159
     160                  self.__selected_image_file = None
     161          #----------------------------------------------------------------
     162          # generic Edit Area mixin API
     163          #----------------------------------------------------------------
     164 -        def _valid_for_save(self):
     165
     166                  valid = True
     167
     168                  if self.mode == u'new':
     169                          if self.__selected_image_file is None:
     170                                  valid = False
     171                                  gmDispatcher.send(signal = 'statustext', msg = _('Must pick an image file for a new tag.'), beep = True)
     172                                  self._BTN_pick_image.SetFocus()
     173
     174                  if self.__selected_image_file is not None:
     175                          try:
     176                                  open(self.__selected_image_file).close()
     177                          except StandardError:
     178                                  valid = False
     179                                  self.__selected_image_file = None
     180                                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot open the image file [%s].') % self.__selected_image_file, beep = True)
     181                                  self._BTN_pick_image.SetFocus()
     182
     183                  if self._TCTRL_description.GetValue().strip() == u'':
     184                          valid = False
     185                          self.display_tctrl_as_valid(self._TCTRL_description, False)
     186                          self._TCTRL_description.SetFocus()
     187                  else:
     188                          self.display_tctrl_as_valid(self._TCTRL_description, True)
     189
     190                  return (valid is True)
     191          #----------------------------------------------------------------
     192 -        def _save_as_new(self):
     193
     194                  dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Creating tag with image'))
     195                  if dbo_conn is None:
     196                          return False
     197
     198                  data = gmDemographicRecord.create_tag_image(description = self._TCTRL_description.GetValue().strip(), link_obj = dbo_conn)
     199                  dbo_conn.close()
     200
     201                  data['filename'] = self._TCTRL_filename.GetValue().strip()
     202                  data.save()
     203                  data.update_image_from_file(filename = self.__selected_image_file)
     204
     205                  # must be done very late or else the property access
     206                  # will refresh the display such that later field
     207                  # access will return empty values
     208                  self.data = data
     209                  return True
     210          #----------------------------------------------------------------
     211 -        def _save_as_update(self):
     212
     213                  # this is somewhat fake as it never actually uses the gm-dbo conn
     214                  # (although it does verify it)
     215                  dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Updating tag with image'))
     216                  if dbo_conn is None:
     217                          return False
     218                  dbo_conn.close()
     219
     220                  self.data['description'] = self._TCTRL_description.GetValue().strip()
     221                  self.data['filename'] = self._TCTRL_filename.GetValue().strip()
     222                  self.data.save()
     223
     224                  if self.__selected_image_file is not None:
     225                          open(self.__selected_image_file).close()
     226                          self.data.update_image_from_file(filename = self.__selected_image_file)
     227                          self.__selected_image_file = None
     228
     229                  return True
     230          #----------------------------------------------------------------
     231 -        def _refresh_as_new(self):
     232                  self._TCTRL_description.SetValue(u'')
     233                  self._TCTRL_filename.SetValue(u'')
     234                  self._BMP_image.SetBitmap(bitmap = wx.EmptyBitmap(100, 100))
     235
     236                  self.__selected_image_file = None
     237
     238                  self._TCTRL_description.SetFocus()
     239          #----------------------------------------------------------------
     240 -        def _refresh_as_new_from_existing(self):
     241                  self._refresh_as_new()
     242          #----------------------------------------------------------------
     243 -        def _refresh_from_existing(self):
     244                  self._TCTRL_description.SetValue(self.data['l10n_description'])
     245                  self._TCTRL_filename.SetValue(gmTools.coalesce(self.data['filename'], u''))
     246                  fname = self.data.export_image2file()
     247                  if fname is None:
     248                          self._BMP_image.SetBitmap(bitmap = wx.EmptyBitmap(100, 100))
     249                  else:
     250                          self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = fname, height = 100))
     251
     252                  self.__selected_image_file = None
     253
     254                  self._TCTRL_description.SetFocus()
     255          #----------------------------------------------------------------
     256          # event handlers
     257          #----------------------------------------------------------------
     258 -        def _on_pick_image_button_pressed(self, event):
     259                  paths = gmTools.gmPaths()
     260                  img_dlg = wx_imagebrowser.ImageDialog(parent = self, set_dir = paths.home_dir)
     261                  img_dlg.Centre()
     262                  if img_dlg.ShowModal() != wx.ID_OK:
     263                          return
     264
     265                  self.__selected_image_file = img_dlg.GetFile()
     266                  self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = self.__selected_image_file, height = 100))
     267                  fdir, fname = os.path.split(self.__selected_image_file)
     268                  self._TCTRL_filename.SetValue(fname)
     269
     270  #============================================================
     271 -def select_patient_tags(parent=None, patient=None):
     272
     273          if parent is None:
     274                  parent = wx.GetApp().GetTopWindow()
     275          #--------------------------------------------------------
     276          def refresh(lctrl):
     277                  tags = patient.tags
     278                  items = [ [
     279                          t['l10n_description'],
     280                          gmTools.coalesce(t['comment'], u'')
     281                  ] for t in tags ]
     282                  lctrl.set_string_items(items)
     283                  #lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
     284                  lctrl.set_data(tags)
     285          #--------------------------------------------------------
     286          def delete(tag):
     287                  do_delete = gmGuiHelpers.gm_show_question (
     288                          title = _('Deleting patient tag'),
     289                          question = _('Do you really want to delete this patient tag ?')
     290                  )
     291                  if not do_delete:
     292                          return False
     293                  patient.remove_tag(tag = tag['pk_identity_tag'])
     294                  return True
     295          #--------------------------------------------------------
     296          def manage_available_tags(tag):
     297                  manage_tag_images(parent = parent)
     298                  return False
     299          #--------------------------------------------------------
     300          msg = _('Tags of patient: %s\n') % patient['description_gender']
     301
     302          return gmListWidgets.get_choices_from_list (
     303                  parent = parent,
     304                  msg = msg,
     305                  caption = _('Showing patient tags'),
     306                  columns = [_('Tag'), _('Comment')],
     307                  single_selection = False,
     308                  delete_callback = delete,
     309                  refresh_callback = refresh,
     310                  left_extra_button = (_('Manage'), _('Manage available tags.'), manage_available_tags)
     311          )
     312  #============================================================
     313  from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl
     314
     315 -class cImageTagPresenterPnl(wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl):
     316
     317 -        def __init__(self, *args, **kwargs):
     318                  wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs)
     319                  self._SZR_bitmaps = self.GetSizer()
     320                  self.__bitmaps = []
     321
     322                  self.__context_popup = wx.Menu()
     323
     324                  item = self.__context_popup.Append(-1, _('&Edit comment'))
     325                  self.Bind(wx.EVT_MENU, self.__edit_tag, item)
     326
     327                  item = self.__context_popup.Append(-1, _('&Remove tag'))
     328                  self.Bind(wx.EVT_MENU, self.__remove_tag, item)
     329          #--------------------------------------------------------
     330          # external API
     331          #--------------------------------------------------------
     332 -        def refresh(self, patient):
     333
     334                  self.clear()
     335
     336                  for tag in patient.get_tags(order_by = u'l10n_description'):
     337                          fname = tag.export_image2file()
     338                          if fname is None:
     339                                  _log.warning('cannot export image data of tag [%s]', tag['l10n_description'])
     340                                  continue
     341                          img = gmGuiHelpers.file2scaled_image(filename = fname, height = 20)
     342                          bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER)
     343                          bmp.SetToolTipString(u'%s%s' % (
     344                                  tag['l10n_description'],
     345                                  gmTools.coalesce(tag['comment'], u'', u'\n\n%s')
     346                          ))
     347                          bmp.tag = tag
     348                          bmp.Bind(wx.EVT_RIGHT_UP, self._on_bitmap_rightclicked)
     349                          # FIXME: add context menu for Delete/Clone/Add/Configure
     350                          self._SZR_bitmaps.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 1)               # | wx.EXPAND
     351                          self.__bitmaps.append(bmp)
     352
     353                  self.GetParent().Layout()
     354          #--------------------------------------------------------
     355 -        def clear(self):
     356                  while len(self._SZR_bitmaps.GetChildren()) > 0:
     357                          self._SZR_bitmaps.Detach(0)
     358  #               for child_idx in range(len(self._SZR_bitmaps.GetChildren())):
     359  #                       self._SZR_bitmaps.Detach(child_idx)
     360                  for bmp in self.__bitmaps:
     361                          bmp.Destroy()
     362                  self.__bitmaps = []
     363          #--------------------------------------------------------
     364          # internal helpers
     365          #--------------------------------------------------------
     366 -        def __remove_tag(self, evt):
     367                  if self.__current_tag is None:
     368                          return
     369                  pat = gmPerson.gmCurrentPatient()
     370                  if not pat.connected:
     371                          return
     372                  pat.remove_tag(tag = self.__current_tag['pk_identity_tag'])
     373          #--------------------------------------------------------
     374 -        def __edit_tag(self, evt):
     375                  if self.__current_tag is None:
     376                          return
     377
     378                  msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description']
     379                  comment = wx.GetTextFromUser (
     380                          message = msg,
     381                          caption = _('Editing tag comment'),
     382                          default_value = gmTools.coalesce(self.__current_tag['comment'], u''),
     383                          parent = self
     384                  )
     385
     386                  if comment == u'':
     387                          return
     388
     389                  if comment.strip() == self.__current_tag['comment']:
     390                          return
     391
     392                  if comment == u' ':
     393                          self.__current_tag['comment'] = None
     394                  else:
     395                          self.__current_tag['comment'] = comment.strip()
     396
     397                  self.__current_tag.save()
     398          #--------------------------------------------------------
     399          # event handlers
     400          #--------------------------------------------------------
     401 -        def _on_bitmap_rightclicked(self, evt):
     402                  self.__current_tag = evt.GetEventObject().tag
     403                  self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition)
     404                  self.__current_tag = None
     405  #============================================================
     406  #============================================================
     407 -class cKOrganizerSchedulePnl(gmDataMiningWidgets.cPatientListingPnl):
     408
     409 -        def __init__(self, *args, **kwargs):
     410
     411                  kwargs['message'] = _("Today's KOrganizer appointments ...")
     412                  kwargs['button_defs'] = [
     413                          {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')},
     414                          {'label': u''},
     415                          {'label': u''},
     416                          {'label': u''},
     417                          {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')}
     418                  ]
     419                  gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs)
     420
     421                  self.fname = os.path.expanduser(os.path.join(gmTools.gmPaths().tmp_dir, 'korganizer2gnumed.csv'))
     422                  self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
     423
     424          #--------------------------------------------------------
     425 -        def _on_BTN_1_pressed(self, event):
     426                  """Reload appointments from KOrganizer."""
     427                  self.reload_appointments()
     428          #--------------------------------------------------------
     429 -        def _on_BTN_5_pressed(self, event):
     430                  """Reload appointments from KOrganizer."""
     431                  found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer')
     432
     433                  if not found:
     434                          gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True)
     435                          return
     436
     437                  gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
     438          #--------------------------------------------------------
     439 -        def reload_appointments(self):
     440                  try: os.remove(self.fname)
     441                  except OSError: pass
     442                  gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True)
     443                  try:
     444                          csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace')
     445                  except IOError:
     446                          gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True)
     447                          return
     448
     449                  csv_lines = gmTools.unicode_csv_reader (
     450                          csv_file,
     451                          delimiter = ','
     452                  )
     453                  # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID
     454                  self._LCTRL_items.set_columns ([
     455                          _('Place'),
     456                          _('Start'),
     457                          u'',
     458                          u'',
     459                          _('Patient'),
     460                          _('Comment')
     461                  ])
     462                  items = []
     463                  data = []
     464                  for line in csv_lines:
     465                          items.append([line[5], line[0], line[1], line[3], line[4], line[6]])
     466                          data.append([line[4], line[7]])
     467
     468                  self._LCTRL_items.set_string_items(items = items)
     469                  self._LCTRL_items.set_column_widths()
     470                  self._LCTRL_items.set_data(data = data)
     471                  self._LCTRL_items.patient_key = 0
     472          #--------------------------------------------------------
     473          # notebook plugins API
     474          #--------------------------------------------------------
     475 -        def repopulate_ui(self):
     476                  self.reload_appointments()
     477  #============================================================
     478  # occupation related widgets / functions
     479  #============================================================
     480 -def edit_occupation():
     481
     482          pat = gmPerson.gmCurrentPatient()
     483          curr_jobs = pat.get_occupations()
     484          if len(curr_jobs) > 0:
     485                  old_job = curr_jobs[0]['l10n_occupation']
     486                  update = curr_jobs[0]['modified_when'].strftime('%m/%Y')
     487          else:
     488                  old_job = u''
     489                  update = u''
     490
     491          msg = _(
     492                  'Please enter the primary occupation of the patient.\n'
     493                  '\n'
     494                  'Currently recorded:\n'
     495                  '\n'
     496                  ' %s (last updated %s)'
     497          ) % (old_job, update)
     498
     499          new_job = wx.GetTextFromUser (
     500                  message = msg,
     501                  caption = _('Editing primary occupation'),
     502                  default_value = old_job,
     503                  parent = None
     504          )
     505          if new_job.strip() == u'':
     506                  return
     507
     508          for job in curr_jobs:
     509                  # unlink all but the new job
     510                  if job['l10n_occupation'] != new_job:
     511                          pat.unlink_occupation(occupation = job['l10n_occupation'])
     512          # and link the new one
     513          pat.link_occupation(occupation = new_job)
     514
     515  #------------------------------------------------------------
     516 -class cOccupationPhraseWheel(gmPhraseWheel.cPhraseWheel):
     517
     518 -        def __init__(self, *args, **kwargs):
     519                  query = u"SELECT distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s"
     520                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     521                  mp.setThresholds(1, 3, 5)
     522                  gmPhraseWheel.cPhraseWheel.__init__ (
     523                          self,
     524                          *args,
     525                          **kwargs
     526                  )
     527                  self.SetToolTipString(_("Type or select an occupation."))
     528                  self.capitalisation_mode = gmTools.CAPS_FIRST
     529                  self.matcher = mp
     530
     531  #============================================================
     532  # identity widgets / functions
     533  #============================================================
     534 -def document_death_of_patient(identity=None):
     535          pass
     536
     537  #------------------------------------------------------------
     538 -def disable_identity(identity=None):
     539          # ask user for assurance
     540          go_ahead = gmGuiHelpers.gm_show_question (
     541                  _('Are you sure you really, positively want\n'
     542                    'to disable the following person ?\n'
     543                    '\n'
     544                    ' %s %s %s\n'
     545                    ' born %s\n'
     546                    '\n'
     547                    '%s\n'
     548                  ) % (
     549                          identity['firstnames'],
     550                          identity['lastnames'],
     551                          identity['gender'],
     552                          identity.get_formatted_dob(),
     553                          gmTools.bool2subst (
     554                                  identity.is_patient,
     555                                  _('This patient DID receive care.'),
     556                                  _('This person did NOT receive care.')
     557                          )
     558                  ),
     559                  _('Disabling person')
     560          )
     561          if not go_ahead:
     562                  return True
     563
     564          # get admin connection
     565          conn = gmAuthWidgets.get_dbowner_connection (
     566                  procedure = _('Disabling patient')
     567          )
     568          # - user cancelled
     569          if conn is False:
     570                  return True
     571          # - error
     572          if conn is None:
     573                  return False
     574
     575          # now disable patient
     576          gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}])
     577
     578          return True
     579
     580  #------------------------------------------------------------
     581  # phrasewheels
     582  #------------------------------------------------------------
     583 -class cLastnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
     584
     585 -        def __init__(self, *args, **kwargs):
     586                  query = u"SELECT distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25"
     587                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     588                  mp.setThresholds(3, 5, 9)
     589                  gmPhraseWheel.cPhraseWheel.__init__ (
     590                          self,
     591                          *args,
     592                          **kwargs
     593                  )
     594                  self.SetToolTipString(_("Type or select a last name (family name/surname)."))
     595                  self.capitalisation_mode = gmTools.CAPS_NAMES
     596                  self.matcher = mp
     597  #------------------------------------------------------------
     598 -class cFirstnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
     599
     600 -        def __init__(self, *args, **kwargs):
     601                  query = u"""
     602                          (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
     603                                  union
     604                          (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
     605                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     606                  mp.setThresholds(3, 5, 9)
     607                  gmPhraseWheel.cPhraseWheel.__init__ (
     608                          self,
     609                          *args,
     610                          **kwargs
     611                  )
     612                  self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name)."))
     613                  self.capitalisation_mode = gmTools.CAPS_NAMES
     614                  self.matcher = mp
     615  #------------------------------------------------------------
     616 -class cNicknamePhraseWheel(gmPhraseWheel.cPhraseWheel):
     617
     618 -        def __init__(self, *args, **kwargs):
     619                  query = u"""
     620                          (SELECT distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20)
     621                                  union
     622                          (SELECT distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20)
     623                                  union
     624                          (SELECT distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)"""
     625                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     626                  mp.setThresholds(3, 5, 9)
     627                  gmPhraseWheel.cPhraseWheel.__init__ (
     628                          self,
     629                          *args,
     630                          **kwargs
     631                  )
     632                  self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name)."))
     633                  # nicknames CAN start with lower case !
     634                  #self.capitalisation_mode = gmTools.CAPS_NAMES
     635                  self.matcher = mp
     636  #------------------------------------------------------------
     637 -class cTitlePhraseWheel(gmPhraseWheel.cPhraseWheel):
     638
     639 -        def __init__(self, *args, **kwargs):
     640                  query = u"SELECT distinct title, title from dem.identity where title %(fragment_condition)s"
     641                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     642                  mp.setThresholds(1, 3, 9)
     643                  gmPhraseWheel.cPhraseWheel.__init__ (
     644                          self,
     645                          *args,
     646                          **kwargs
     647                  )
     648                  self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !"))
     649                  self.matcher = mp
     650  #------------------------------------------------------------
     651 -class cGenderSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
     652          """Let user select a gender."""
     653
     654          _gender_map = None
     655
     656 -        def __init__(self, *args, **kwargs):
     657
     658                  if cGenderSelectionPhraseWheel._gender_map is None:
     659                          cmd = u"""
     660                                  SELECT tag, l10n_label, sort_weight
     661                                  from dem.v_gender_labels
     662                                  order by sort_weight desc"""
     663                          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True)
     664                          cGenderSelectionPhraseWheel._gender_map = {}
     665                          for gender in rows:
     666                                  cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = {
     667                                          'data': gender[idx['tag']],
     668                                          'field_label': gender[idx['l10n_label']],
     669                                          'list_label': gender[idx['l10n_label']],
     670                                          'weight': gender[idx['sort_weight']]
     671                                  }
     672
     673                  mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values())
     674                  mp.setThresholds(1, 1, 3)
     675
     676                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     677                  self.selection_only = True
     678                  self.matcher = mp
     679                  self.picklist_delay = 50
     680  #------------------------------------------------------------
     681 -class cExternalIDTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
     682
     683 -        def __init__(self, *args, **kwargs):
     684                  query = u"""
     685                          SELECT DISTINCT ON (list_label)
     686                                  pk AS data,
     687                                  name AS field_label,
     688                                  name || coalesce(' (' || issuer || ')', '') as list_label
     689                          FROM dem.enum_ext_id_types
     690                          WHERE name %(fragment_condition)s
     691                          ORDER BY list_label
     692                          LIMIT 25
     693                  """
     694                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     695                  mp.setThresholds(1, 3, 5)
     696                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     697                  self.SetToolTipString(_("Enter or select a type for the external ID."))
     698                  self.matcher = mp
     699          #--------------------------------------------------------
     700 -        def _get_data_tooltip(self):
     701                  if self.GetData() is None:
     702                          return None
     703                  return self._data.values()[0]['list_label']
     704  #------------------------------------------------------------
     705 -class cExternalIDIssuerPhraseWheel(gmPhraseWheel.cPhraseWheel):
     706
     707 -        def __init__(self, *args, **kwargs):
     708                  query = u"""
     709  SELECT distinct issuer, issuer
     710  from dem.enum_ext_id_types
     711  where issuer %(fragment_condition)s
     712  order by issuer limit 25"""
     713                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     714                  mp.setThresholds(1, 3, 5)
     715                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     716                  self.SetToolTipString(_("Type or select an ID issuer."))
     717                  self.capitalisation_mode = gmTools.CAPS_FIRST
     718                  self.matcher = mp
     719  #------------------------------------------------------------
     720  # edit areas
     721  #------------------------------------------------------------
     722  from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl
     723
     724 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
     725          """An edit area for editing/creating external IDs.
     726
     727          Does NOT act on/listen to the current patient.
     728          """
     729 -        def __init__(self, *args, **kwargs):
     730
     731                  try:
     732                          data = kwargs['external_id']
     733                          del kwargs['external_id']
     734                  except:
     735                          data = None
     736
     737                  wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs)
     738                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     739
     740                  self.identity = None
     741
     742                  self.mode = 'new'
     743                  self.data = data
     744                  if data is not None:
     745                          self.mode = 'edit'
     746
     747                  self.__init_ui()
     748          #--------------------------------------------------------
     749 -        def __init_ui(self):
     750                  self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
     751          #----------------------------------------------------------------
     752          # generic Edit Area mixin API
     753          #----------------------------------------------------------------
     754 -        def _valid_for_save(self):
     755                  validity = True
     756
     757                  # do not test .GetData() because adding external
     758                  # IDs will create types as necessary
     759                  #if self._PRW_type.GetData() is None:
     760                  if self._PRW_type.GetValue().strip() == u'':
     761                          validity = False
     762                          self._PRW_type.display_as_valid(False)
     763                          self._PRW_type.SetFocus()
     764                  else:
     765                          self._PRW_type.display_as_valid(True)
     766
     767                  if self._TCTRL_value.GetValue().strip() == u'':
     768                          validity = False
     769                          self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = False)
     770                  else:
     771                          self.display_tctrl_as_valid(tctrl = self._TCTRL_value, valid = True)
     772
     773                  return validity
     774          #----------------------------------------------------------------
     775 -        def _save_as_new(self):
     776                  data = {}
     777                  data['pk_type'] = None
     778                  data['name'] = self._PRW_type.GetValue().strip()
     779                  data['value'] = self._TCTRL_value.GetValue().strip()
     780                  data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u'')
     781                  data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
     782
     783                  self.identity.add_external_id (
     784                          type_name = data['name'],
     785                          value = data['value'],
     786                          issuer = data['issuer'],
     787                          comment = data['comment']
     788                  )
     789
     790                  self.data = data
     791                  return True
     792          #----------------------------------------------------------------
     793 -        def _save_as_update(self):
     794                  self.data['name'] = self._PRW_type.GetValue().strip()
     795                  self.data['value'] = self._TCTRL_value.GetValue().strip()
     796                  self.data['issuer'] = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u'')
     797                  self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
     798
     799                  self.identity.update_external_id (
     800                          pk_id = self.data['pk_id'],
     801                          type = self.data['name'],
     802                          value = self.data['value'],
     803                          issuer = self.data['issuer'],
     804                          comment = self.data['comment']
     805                  )
     806
     807                  return True
     808          #----------------------------------------------------------------
     809 -        def _refresh_as_new(self):
     810                  self._PRW_type.SetText(value = u'', data = None)
     811                  self._TCTRL_value.SetValue(u'')
     812                  self._PRW_issuer.SetText(value = u'', data = None)
     813                  self._TCTRL_comment.SetValue(u'')
     814          #----------------------------------------------------------------
     815 -        def _refresh_as_new_from_existing(self):
     816                  self._refresh_as_new()
     817                  self._PRW_issuer.SetText(self.data['issuer'])
     818          #----------------------------------------------------------------
     819 -        def _refresh_from_existing(self):
     820                  self._PRW_type.SetText(value = self.data['name'], data = self.data['pk_type'])
     821                  self._TCTRL_value.SetValue(self.data['value'])
     822                  self._PRW_issuer.SetText(self.data['issuer'])
     823                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
     824          #----------------------------------------------------------------
     825          # internal helpers
     826          #----------------------------------------------------------------
     827 -        def _on_type_set(self):
     828                  """Set the issuer according to the selected type.
     829
     830                  Matches are fetched from existing records in backend.
     831                  """
     832                  pk_curr_type = self._PRW_type.GetData()
     833                  if pk_curr_type is None:
     834                          return True
     835                  rows, idx = gmPG2.run_ro_queries(queries = [{
     836                          'cmd': u"SELECT issuer from dem.enum_ext_id_types where pk = %s",
     837                          'args': [pk_curr_type]
     838                  }])
     839                  if len(rows) == 0:
     840                          return True
     841                  wx.CallAfter(self._PRW_issuer.SetText, rows[0][0])
     842                  return True
     843
     844  #============================================================
     845  # identity widgets
     846  #------------------------------------------------------------
     847 -def _empty_dob_allowed():
     848          allow_empty_dob = gmGuiHelpers.gm_show_question (
     849                  _(
     850                          'Are you sure you want to leave this person\n'
     851                          'without a valid date of birth ?\n'
     852                          '\n'
     853                          'This can be useful for temporary staff members\n'
     854                          'but will provoke nag screens if this person\n'
     855                          'becomes a patient.\n'
     856                  ),
     857                  _('Validating date of birth')
     858          )
     859          return allow_empty_dob
     860  #------------------------------------------------------------
     861 -def _validate_dob_field(dob_prw):
     862
     863          # valid timestamp ?
     864          if dob_prw.is_valid_timestamp(allow_empty = False):                     # properly colors the field
     865                  dob = dob_prw.date
     866                  # but year also usable ?
     867                  if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
     868                          return True
     869
     870                  if dob.year < 1900:
     871                          msg = _(
     872                                  'DOB: %s\n'
     873                                  '\n'
     874                                  'While this is a valid point in time Python does\n'
     875                                  'not know how to deal with it.\n'
     876                                  '\n'
     877                                  'We suggest using January 1st 1901 instead and adding\n'
     878                                  'the true date of birth to the patient comment.\n'
     879                                  '\n'
     880                                  'Sorry for the inconvenience %s'
     881                          ) % (dob, gmTools.u_frowning_face)
     882                  else:
     883                          msg = _(
     884                                  'DOB: %s\n'
     885                                  '\n'
     886                                  'Date of birth in the future !'
     887                          ) % dob
     888                  gmGuiHelpers.gm_show_error (
     889                          msg,
     890                          _('Validating date of birth')
     891                  )
     892                  dob_prw.display_as_valid(False)
     893                  dob_prw.SetFocus()
     894                  return False
     895
     896          # invalid timestamp but not empty
     897          if dob_prw.GetValue().strip() != u'':
     898                  dob_prw.display_as_valid(False)
     899                  gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of birth.'))
     900                  dob_prw.SetFocus()
     901                  return False
     902
     903          # empty DOB field
     904          dob_prw.display_as_valid(False)
     905          return True
     906
     907  #------------------------------------------------------------
     908 -def _validate_tob_field(ctrl):
     909
     910          val = ctrl.GetValue().strip()
     911
     912          if val == u'':
     913                  return True
     914
     915          converted, hours = gmTools.input2int(val[:2], 0, 23)
     916          if not converted:
     917                  return False
     918
     919          converted, minutes = gmTools.input2int(val[3:5], 0, 59)
     920          if not converted:
     921                  return False
     922
     923          return True
     924
     925  #------------------------------------------------------------
     926  from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
     927
     928 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
     929          """An edit area for editing/creating title/gender/dob/dod etc."""
     930
     931 -        def __init__(self, *args, **kwargs):
     932
     933                  try:
     934                          data = kwargs['identity']
     935                          del kwargs['identity']
     936                  except KeyError:
     937                          data = None
     938
     939                  wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
     940                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     941
     942                  self.mode = 'new'
     943                  self.data = data
     944                  if data is not None:
     945                          self.mode = 'edit'
     946
     947  #               self.__init_ui()
     948          #----------------------------------------------------------------
     949  #       def __init_ui(self):
     950  #               # adjust phrasewheels etc
     951          #----------------------------------------------------------------
     952          # generic Edit Area mixin API
     953          #----------------------------------------------------------------
     954 -        def _valid_for_save(self):
     955
     956                  has_error = False
     957
     958                  if self._PRW_gender.GetData() is None:
     959                          self._PRW_gender.SetFocus()
     960                          has_error = True
     961
     962                  if self.data is not None:
     963                          if not _validate_dob_field(self._PRW_dob):
     964                                  has_error = True
     965
     966                  # TOB validation
     967                  if _validate_tob_field(self._TCTRL_tob):
     968                          self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
     969                  else:
     970                          has_error = True
     971                          self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
     972
     973                  if not self._PRW_dod.is_valid_timestamp(allow_empty = True):
     974                          gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.'))
     975                          self._PRW_dod.SetFocus()
     976                          has_error = True
     977
     978                  return (has_error is False)
     979          #----------------------------------------------------------------
     980 -        def _save_as_new(self):
     981                  # not used yet
     982                  return False
     983          #----------------------------------------------------------------
     984 -        def _save_as_update(self):
     985
     986                  if self._PRW_dob.GetValue().strip() == u'':
     987                          if not _empty_dob_allowed():
     988                                  return False
     989                          self.data['dob'] = None
     990                  else:
     991                          self.data['dob'] = self._PRW_dob.GetData()
     992                  self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
     993                  val = self._TCTRL_tob.GetValue().strip()
     994                  if val == u'':
     995                          self.data['tob'] = None
     996                  else:
     997                          self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
     998                  self.data['gender'] = self._PRW_gender.GetData()
     999                  self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'')
    1000                  self.data['deceased'] = self._PRW_dod.GetData()
    1001                  self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
    1002
    1003                  self.data.save()
    1004                  return True
    1005          #----------------------------------------------------------------
    1006 -        def _refresh_as_new(self):
    1007                  pass
    1008          #----------------------------------------------------------------
    1009 -        def _refresh_from_existing(self):
    1010
    1011                  self._LBL_info.SetLabel(u'ID: #%s' % (
    1012                          self.data.ID
    1013                          # FIXME: add 'deleted' status
    1014                  ))
    1015                  if self.data['dob'] is None:
    1016                          val = u''
    1017                  else:
    1018                          val = gmDateTime.pydt_strftime (
    1019                                  self.data['dob'],
    1020                                  format = '%Y-%m-%d',
    1021                                  accuracy = gmDateTime.acc_minutes
    1022                          )
    1023                  self._PRW_dob.SetText(value = val, data = self.data['dob'])
    1024                  self._CHBOX_estimated_dob.SetValue(self.data['dob_is_estimated'])
    1025                  if self.data['tob'] is None:
    1026                          self._TCTRL_tob.SetValue(u'')
    1027                  else:
    1028                          self._TCTRL_tob.SetValue(self.data['tob'].strftime('%H:%M'))
    1029                  if self.data['deceased'] is None:
    1030                          val = u''
    1031                  else:
    1032                          val = gmDateTime.pydt_strftime (
    1033                                  self.data['deceased'],
    1034                                  format = '%Y-%m-%d %H:%M',
    1035                                  accuracy = gmDateTime.acc_minutes
    1036                          )
    1037                  self._PRW_dod.SetText(value = val, data = self.data['deceased'])
    1038                  self._PRW_gender.SetData(self.data['gender'])
    1039                  #self._PRW_ethnicity.SetValue()
    1040                  self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u''))
    1041                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
    1042          #----------------------------------------------------------------
    1043 -        def _refresh_as_new_from_existing(self):
    1044                  pass
    1045  #------------------------------------------------------------
    1046  from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
    1047
    1048 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
    1049          """An edit area for editing/creating names of people.
    1050
    1051          Does NOT act on/listen to the current patient.
    1052          """
    1053 -        def __init__(self, *args, **kwargs):
    1054
    1055                  try:
    1056                          data = kwargs['name']
    1057                          identity = gmPerson.cIdentity(aPK_obj = data['pk_identity'])
    1058                          del kwargs['name']
    1059                  except KeyError:
    1060                          data = None
    1061                          identity = kwargs['identity']
    1062                          del kwargs['identity']
    1063
    1064                  wxgPersonNameEAPnl.wxgPersonNameEAPnl.__init__(self, *args, **kwargs)
    1065                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1066
    1067                  self.__identity = identity
    1068
    1069                  self.mode = 'new'
    1070                  self.data = data
    1071                  if data is not None:
    1072                          self.mode = 'edit'
    1073
    1074                  #self.__init_ui()
    1075          #----------------------------------------------------------------
    1076  #       def __init_ui(self):
    1077  #               # adjust phrasewheels etc
    1078          #----------------------------------------------------------------
    1079          # generic Edit Area mixin API
    1080          #----------------------------------------------------------------
    1081 -        def _valid_for_save(self):
    1082                  validity = True
    1083
    1084                  if self._PRW_lastname.GetValue().strip() == u'':
    1085                          validity = False
    1086                          self._PRW_lastname.display_as_valid(False)
    1087                          self._PRW_lastname.SetFocus()
    1088                  else:
    1089                          self._PRW_lastname.display_as_valid(True)
    1090
    1091                  if self._PRW_firstname.GetValue().strip() == u'':
    1092                          validity = False
    1093                          self._PRW_firstname.display_as_valid(False)
    1094                          self._PRW_firstname.SetFocus()
    1095                  else:
    1096                          self._PRW_firstname.display_as_valid(True)
    1097
    1098                  return validity
    1099          #----------------------------------------------------------------
    1100 -        def _save_as_new(self):
    1101
    1102                  first = self._PRW_firstname.GetValue().strip()
    1103                  last = self._PRW_lastname.GetValue().strip()
    1104                  active = self._CHBOX_active.GetValue()
    1105
    1106                  try:
    1107                          data = self.__identity.add_name(first, last, active)
    1108                  except gmPG2.dbapi.IntegrityError as exc:
    1109                          _log.exception('cannot save new name')
    1110                          gmGuiHelpers.gm_show_error (
    1111                                  aTitle = _('Adding name'),
    1112                                  aMessage = _(
    1113                                          'Cannot add this name to the patient !\n'
    1114                                          '\n'
    1115                                          ' %s'
    1116                                  ) % str(exc)
    1117                          )
    1118                          return False
    1119
    1120                  old_nick = self.__identity['active_name']['preferred']
    1121                  new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
    1122                  if active:
    1123                          data['preferred'] = gmTools.coalesce(new_nick, old_nick)
    1124                  else:
    1125                          data['preferred'] = new_nick
    1126                  data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
    1127                  data.save()
    1128
    1129                  self.data = data
    1130                  return True
    1131          #----------------------------------------------------------------
    1132 -        def _save_as_update(self):
    1133                  """The knack here is that we can only update a few fields.
    1134
    1135                  Otherwise we need to clone the name and update that.
    1136                  """
    1137                  first = self._PRW_firstname.GetValue().strip()
    1138                  last = self._PRW_lastname.GetValue().strip()
    1139                  active = self._CHBOX_active.GetValue()
    1140
    1141                  current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
    1142                  new_name = first + last
    1143
    1144                  # editable fields only ?
    1145                  if new_name == current_name:
    1146                          self.data['active_name'] = self._CHBOX_active.GetValue()
    1147                          self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
    1148                          self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
    1149                          self.data.save()
    1150                  # else clone name and update that
    1151                  else:
    1152                          try:
    1153                                  name = self.__identity.add_name(first, last, active)
    1154                          except gmPG2.dbapi.IntegrityError as exc:
    1155                                  _log.exception('cannot clone name when editing existing name')
    1156                                  gmGuiHelpers.gm_show_error (
    1157                                          aTitle = _('Editing name'),
    1158                                          aMessage = _(
    1159                                                  'Cannot clone a copy of this name !\n'
    1160                                                  '\n'
    1161                                                  ' %s'
    1162                                          ) % str(exc)
    1163                                  )
    1164                                  return False
    1165                          name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'')
    1166                          name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')
    1167                          name.save()
    1168                          self.data = name
    1169
    1170                  return True
    1171          #----------------------------------------------------------------
    1172 -        def _refresh_as_new(self):
    1173                  self._PRW_firstname.SetText(value = u'', data = None)
    1174                  self._PRW_lastname.SetText(value = u'', data = None)
    1175                  self._PRW_nick.SetText(value = u'', data = None)
    1176                  self._TCTRL_comment.SetValue(u'')
    1177                  self._CHBOX_active.SetValue(False)
    1178
    1179                  self._PRW_firstname.SetFocus()
    1180          #----------------------------------------------------------------
    1181 -        def _refresh_as_new_from_existing(self):
    1182                  self._refresh_as_new()
    1183                  self._PRW_firstname.SetText(value = u'', data = None)
    1184                  self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], u''))
    1185
    1186                  self._PRW_lastname.SetFocus()
    1187          #----------------------------------------------------------------
    1188 -        def _refresh_from_existing(self):
    1189                  self._PRW_firstname.SetText(self.data['firstnames'])
    1190                  self._PRW_lastname.SetText(self.data['lastnames'])
    1191                  self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], u''))
    1192                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
    1193                  self._CHBOX_active.SetValue(self.data['active_name'])
    1194
    1195                  self._TCTRL_comment.SetFocus()
    1196  #------------------------------------------------------------
    1197  # list manager
    1198  #------------------------------------------------------------
    1199 -class cPersonNamesManagerPnl(gmListWidgets.cGenericListManagerPnl):
    1200          """A list for managing a person's names.
    1201
    1202          Does NOT act on/listen to the current patient.
    1203          """
    1204 -        def __init__(self, *args, **kwargs):
    1205
    1206                  try:
    1207                          self.__identity = kwargs['identity']
    1208                          del kwargs['identity']
    1209                  except KeyError:
    1210                          self.__identity = None
    1211
    1212                  gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
    1213
    1214                  self.new_callback = self._add_name
    1215                  self.edit_callback = self._edit_name
    1216                  self.delete_callback = self._del_name
    1217                  self.refresh_callback = self.refresh
    1218
    1219                  self.__init_ui()
    1220                  self.refresh()
    1221          #--------------------------------------------------------
    1222          # external API
    1223          #--------------------------------------------------------
    1224 -        def refresh(self, *args, **kwargs):
    1225                  if self.__identity is None:
    1226                          self._LCTRL_items.set_string_items()
    1227                          return
    1228
    1229                  names = self.__identity.get_names()
    1230                  self._LCTRL_items.set_string_items (
    1231                          items = [ [
    1232                                          gmTools.bool2str(n['active_name'], 'X', ''),
    1233                                          n['lastnames'],
    1234                                          n['firstnames'],
    1235                                          gmTools.coalesce(n['preferred'], u''),
    1236                                          gmTools.coalesce(n['comment'], u'')
    1237                                  ] for n in names ]
    1238                  )
    1239                  self._LCTRL_items.set_column_widths()
    1240                  self._LCTRL_items.set_data(data = names)
    1241          #--------------------------------------------------------
    1242          # internal helpers
    1243          #--------------------------------------------------------
    1244 -        def __init_ui(self):
    1245                  self._LCTRL_items.set_columns(columns = [
    1246                          _('Active'),
    1247                          _('Lastname'),
    1248                          _('Firstname(s)'),
    1249                          _('Preferred Name'),
    1250                          _('Comment')
    1251                  ])
    1252          #--------------------------------------------------------
    1253 -        def _add_name(self):
    1254                  #ea = cPersonNameEAPnl(self, -1, name = self.__identity.get_active_name())
    1255                  ea = cPersonNameEAPnl(self, -1, identity = self.__identity)
    1256                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1257                  dlg.SetTitle(_('Adding new name'))
    1258                  if dlg.ShowModal() == wx.ID_OK:
    1259                          dlg.Destroy()
    1260                          return True
    1261                  dlg.Destroy()
    1262                  return False
    1263          #--------------------------------------------------------
    1264 -        def _edit_name(self, name):
    1265                  ea = cPersonNameEAPnl(self, -1, name = name)
    1266                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1267                  dlg.SetTitle(_('Editing name'))
    1268                  if dlg.ShowModal() == wx.ID_OK:
    1269                          dlg.Destroy()
    1270                          return True
    1271                  dlg.Destroy()
    1272                  return False
    1273          #--------------------------------------------------------
    1274 -        def _del_name(self, name):
    1275
    1276                  if len(self.__identity.get_names()) == 1:
    1277                          gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
    1278                          return False
    1279
    1280                  if name['active_name']:
    1281                          gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
    1282                          return False
    1283
    1284                  go_ahead = gmGuiHelpers.gm_show_question (
    1285                          _(      'It is often advisable to keep old names around and\n'
    1286                                  'just create a new "currently active" name.\n'
    1287                                  '\n'
    1288                                  'This allows finding the patient by both the old\n'
    1289                                  'and the new name (think before/after marriage).\n'
    1290                                  '\n'
    1291                                  'Do you still want to really delete\n'
    1292                                  "this name from the patient ?"
    1293                          ),
    1294                          _('Deleting name')
    1295                  )
    1296                  if not go_ahead:
    1297                          return False
    1298
    1299                  self.__identity.delete_name(name = name)
    1300                  return True
    1301          #--------------------------------------------------------
    1302          # properties
    1303          #--------------------------------------------------------
    1304 -        def _get_identity(self):
    1305                  return self.__identity
    1306
    1307 -        def _set_identity(self, identity):
    1308                  self.__identity = identity
    1309                  self.refresh()
    1310
    1311          identity = property(_get_identity, _set_identity)
    1312  #------------------------------------------------------------
    1313 -class cPersonIDsManagerPnl(gmListWidgets.cGenericListManagerPnl):
    1314          """A list for managing a person's external IDs.
    1315
    1316          Does NOT act on/listen to the current patient.
    1317          """
    1318 -        def __init__(self, *args, **kwargs):
    1319
    1320                  try:
    1321                          self.__identity = kwargs['identity']
    1322                          del kwargs['identity']
    1323                  except KeyError:
    1324                          self.__identity = None
    1325
    1326                  gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
    1327
    1328                  self.new_callback = self._add_id
    1329                  self.edit_callback = self._edit_id
    1330                  self.delete_callback = self._del_id
    1331                  self.refresh_callback = self.refresh
    1332
    1333                  self.__init_ui()
    1334                  self.refresh()
    1335          #--------------------------------------------------------
    1336          # external API
    1337          #--------------------------------------------------------
    1338 -        def refresh(self, *args, **kwargs):
    1339                  if self.__identity is None:
    1340                          self._LCTRL_items.set_string_items()
    1341                          return
    1342
    1343                  ids = self.__identity.get_external_ids()
    1344                  self._LCTRL_items.set_string_items (
    1345                          items = [ [
    1346                                          i['name'],
    1347                                          i['value'],
    1348                                          gmTools.coalesce(i['issuer'], u''),
    1349                                          gmTools.coalesce(i['comment'], u'')
    1350                                  ] for i in ids
    1351                          ]
    1352                  )
    1353                  self._LCTRL_items.set_column_widths()
    1354                  self._LCTRL_items.set_data(data = ids)
    1355          #--------------------------------------------------------
    1356          # internal helpers
    1357          #--------------------------------------------------------
    1358 -        def __init_ui(self):
    1359                  self._LCTRL_items.set_columns(columns = [
    1360                          _('ID type'),
    1361                          _('Value'),
    1362                          _('Issuer'),
    1363                          _('Comment')
    1364                  ])
    1365          #--------------------------------------------------------
    1366 -        def _add_id(self):
    1367                  ea = cExternalIDEditAreaPnl(self, -1)
    1368                  ea.identity = self.__identity
    1369                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea)
    1370                  dlg.SetTitle(_('Adding new external ID'))
    1371                  if dlg.ShowModal() == wx.ID_OK:
    1372                          dlg.Destroy()
    1373                          return True
    1374                  dlg.Destroy()
    1375                  return False
    1376          #--------------------------------------------------------
    1377 -        def _edit_id(self, ext_id):
    1378                  ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
    1379                  ea.identity = self.__identity
    1380                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1381                  dlg.SetTitle(_('Editing external ID'))
    1382                  if dlg.ShowModal() == wx.ID_OK:
    1383                          dlg.Destroy()
    1384                          return True
    1385                  dlg.Destroy()
    1386                  return False
    1387          #--------------------------------------------------------
    1388 -        def _del_id(self, ext_id):
    1389                  go_ahead = gmGuiHelpers.gm_show_question (
    1390                          _(      'Do you really want to delete this\n'
    1391                                  'external ID from the patient ?'),
    1392                          _('Deleting external ID')
    1393                  )
    1394                  if not go_ahead:
    1395                          return False
    1396                  self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
    1397                  return True
    1398          #--------------------------------------------------------
    1399          # properties
    1400          #--------------------------------------------------------
    1401 -        def _get_identity(self):
    1402                  return self.__identity
    1403
    1404 -        def _set_identity(self, identity):
    1405                  self.__identity = identity
    1406                  self.refresh()
    1407
    1408          identity = property(_get_identity, _set_identity)
    1409  #------------------------------------------------------------
    1410  # integrated panels
    1411  #------------------------------------------------------------
    1412  from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
    1413
    1414 -class cPersonIdentityManagerPnl(wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl):
    1415          """A panel for editing identity data for a person.
    1416
    1417          - provides access to:
    1418            - identity EA
    1419            - name list manager
    1420            - external IDs list manager
    1421
    1422          Does NOT act on/listen to the current patient.
    1423          """
    1424 -        def __init__(self, *args, **kwargs):
    1425
    1426                  wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
    1427
    1428                  self.__identity = None
    1429                  self.refresh()
    1430          #--------------------------------------------------------
    1431          # external API
    1432          #--------------------------------------------------------
    1433 -        def refresh(self):
    1434                  self._PNL_names.identity = self.__identity
    1435                  self._PNL_ids.identity = self.__identity
    1436                  # this is an Edit Area:
    1437                  self._PNL_identity.mode = 'new'
    1438                  self._PNL_identity.data = self.__identity
    1439                  if self.__identity is not None:
    1440                          self._PNL_identity.mode = 'edit'
    1441                          self._PNL_identity._refresh_from_existing()
    1442          #--------------------------------------------------------
    1443          # properties
    1444          #--------------------------------------------------------
    1445 -        def _get_identity(self):
    1446                  return self.__identity
    1447
    1448 -        def _set_identity(self, identity):
    1449                  self.__identity = identity
    1450                  self.refresh()
    1451
    1452          identity = property(_get_identity, _set_identity)
    1453          #--------------------------------------------------------
    1454          # event handlers
    1455          #--------------------------------------------------------
    1456 -        def _on_save_identity_details_button_pressed(self, event):
    1457                  if not self._PNL_identity.save():
    1458                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot save identity. Incomplete information.'), beep = True)
    1459                  #self._PNL_identity.refresh()
    1460          #--------------------------------------------------------
    1461 -        def _on_reload_identity_button_pressed(self, event):
    1462                  self._PNL_identity.refresh()
    1463
    1464  #============================================================
    1465  from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
    1466
    1467 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
    1468 -        def __init__(self, *args, **kwargs):
    1469
    1470                  wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
    1471
    1472                  self.__identity = None
    1473                  self._PRW_provider.selection_only = False
    1474                  self.refresh()
    1475          #--------------------------------------------------------
    1476          # external API
    1477          #--------------------------------------------------------
    1478 -        def refresh(self):
    1479
    1480                  tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
    1481
    1482                  if self.__identity is None:
    1483                          self._TCTRL_er_contact.SetValue(u'')
    1484                          self._TCTRL_person.person = None
    1485                          self._TCTRL_person.SetToolTipString(tt)
    1486
    1487                          self._PRW_provider.SetText(value = u'', data = None)
    1488                          return
    1489
    1490                  self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u''))
    1491                  if self.__identity['pk_emergency_contact'] is not None:
    1492                          ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact'])
    1493                          self._TCTRL_person.person = ident
    1494                          tt = u'%s\n\n%s\n\n%s' % (
    1495                                  tt,
    1496                                  ident['description_gender'],
    1497                                  u'\n'.join([
    1498                                          u'%s: %s%s' % (
    1499                                                  c['l10n_comm_type'],
    1500                                                  c['url'],
    1501                                                  gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'')
    1502                                          )
    1503                                          for c in ident.get_comm_channels()
    1504                                  ])
    1505                          )
    1506                  else:
    1507                          self._TCTRL_person.person = None
    1508
    1509                  self._TCTRL_person.SetToolTipString(tt)
    1510
    1511                  if self.__identity['pk_primary_provider'] is None:
    1512                          self._PRW_provider.SetText(value = u'', data = None)
    1513                  else:
    1514                          self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
    1515          #--------------------------------------------------------
    1516          # properties
    1517          #--------------------------------------------------------
    1518 -        def _get_identity(self):
    1519                  return self.__identity
    1520
    1521 -        def _set_identity(self, identity):
    1522                  self.__identity = identity
    1523                  self.refresh()
    1524
    1525          identity = property(_get_identity, _set_identity)
    1526          #--------------------------------------------------------
    1527          # event handlers
    1528          #--------------------------------------------------------
    1529 -        def _on_save_button_pressed(self, event):
    1530                  if self.__identity is not None:
    1531                          self.__identity['emergency_contact'] = self._TCTRL_er_contact.GetValue().strip()
    1532                          if self._TCTRL_person.person is not None:
    1533                                  self.__identity['pk_emergency_contact'] = self._TCTRL_person.person.ID
    1534                          if self._PRW_provider.GetValue().strip == u'':
    1535                                  self.__identity['pk_primary_provider'] = None
    1536                          else:
    1537                                  self.__identity['pk_primary_provider'] = self._PRW_provider.GetData()
    1538
    1539                          self.__identity.save()
    1540                          gmDispatcher.send(signal = 'statustext', msg = _('Emergency data and primary provider saved.'), beep = False)
    1541
    1542                  event.Skip()
    1543          #--------------------------------------------------------
    1544 -        def _on_reload_button_pressed(self, event):
    1545                  self.refresh()
    1546          #--------------------------------------------------------
    1547 -        def _on_remove_contact_button_pressed(self, event):
    1548                  event.Skip()
    1549
    1550                  if self.__identity is None:
    1551                          return
    1552
    1553                  self._TCTRL_person.person = None
    1554
    1555                  self.__identity['pk_emergency_contact'] = None
    1556                  self.__identity.save()
    1557          #--------------------------------------------------------
    1558 -        def _on_button_activate_contact_pressed(self, event):
    1559                  ident = self._TCTRL_person.person
    1560                  if ident is not None:
    1561                          from Gnumed.wxpython import gmPatSearchWidgets
    1562                          gmPatSearchWidgets.set_active_patient(patient = ident, forced_reload = False)
    1563
    1564                  event.Skip()
    1565
    1566  #============================================================
    1567  # patient demographics editing classes
    1568  #============================================================
    1569 -class cPersonDemographicsEditorNb(wx.Notebook):
    1570          """Notebook displaying demographics editing pages:
    1571
    1572                  - Identity (as per Jim/Rogerio 12/2011)
    1573                  - Contacts (addresses, phone numbers, etc)
    1574                  - Social network (significant others, GP, etc)
    1575
    1576          Does NOT act on/listen to the current patient.
    1577          """
    1578          #--------------------------------------------------------
    1579 -        def __init__(self, parent, id):
    1580
    1581                  wx.Notebook.__init__ (
    1582                          self,
    1583                          parent = parent,
    1584                          id = id,
    1585                          style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
    1586                          name = self.__class__.__name__
    1587                  )
    1588
    1589                  self.__identity = None
    1590                  self.__do_layout()
    1591                  self.SetSelection(0)
    1592          #--------------------------------------------------------
    1593          # public API
    1594          #--------------------------------------------------------
    1595 -        def refresh(self):
    1596                  """Populate fields in pages with data from model."""
    1597                  for page_idx in range(self.GetPageCount()):
    1598                          page = self.GetPage(page_idx)
    1599                          page.identity = self.__identity
    1600
    1601                  return True
    1602          #--------------------------------------------------------
    1603          # internal API
    1604          #--------------------------------------------------------
    1605 -        def __do_layout(self):
    1606                  """Build patient edition notebook pages."""
    1607
    1608                  # identity page
    1609                  new_page = cPersonIdentityManagerPnl(self, -1)
    1610                  new_page.identity = self.__identity
    1611                  self.AddPage (
    1612                          page = new_page,
    1613                          text = _('Identity'),
    1614                          select = False
    1615                  )
    1616
    1617                  # contacts page
    1618                  new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
    1619                  new_page.identity = self.__identity
    1620                  self.AddPage (
    1621                          page = new_page,
    1622                          text = _('Contacts'),
    1623                          select = True
    1624                  )
    1625
    1626                  # social network page
    1627                  new_page = cPersonSocialNetworkManagerPnl(self, -1)
    1628                  new_page.identity = self.__identity
    1629                  self.AddPage (
    1630                          page = new_page,
    1631                          text = _('Social network'),
    1632                          select = False
    1633                  )
    1634          #--------------------------------------------------------
    1635          # properties
    1636          #--------------------------------------------------------
    1637 -        def _get_identity(self):
    1638                  return self.__identity
    1639
    1640 -        def _set_identity(self, identity):
    1641                  self.__identity = identity
    1642
    1643          identity = property(_get_identity, _set_identity)
    1644  #============================================================
    1645  # old occupation widgets
    1646  #============================================================
    1647  # FIXME: support multiple occupations
    1648  # FIXME: redo with wxGlade
    1649
    1650 -class cPatOccupationsPanel(wx.Panel):
    1651          """Page containing patient occupations edition fields.
    1652          """
    1653 -        def __init__(self, parent, id, ident=None):
    1654                  """
    1655                  Creates a new instance of BasicPatDetailsPage
    1656                  @param parent - The parent widget
    1657                  @type parent - A wx.Window instance
    1658                  @param id - The widget id
    1659                  @type id - An integer
    1660                  """
    1661                  wx.Panel.__init__(self, parent, id)
    1662                  self.__ident = ident
    1663                  self.__do_layout()
    1664          #--------------------------------------------------------
    1665 -        def __do_layout(self):
    1666                  PNL_form = wx.Panel(self, -1)
    1667                  # occupation
    1668                  STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
    1669                  self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1)
    1670                  self.PRW_occupation.SetToolTipString(_("primary occupation of the patient"))
    1671                  # known since
    1672                  STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
    1673                  self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
    1674
    1675                  # layout input widgets
    1676                  SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
    1677                  SZR_input.AddGrowableCol(1)
    1678                  SZR_input.Add(STT_occupation, 0, wx.SHAPED)
    1679                  SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
    1680                  SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
    1681                  SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
    1682                  PNL_form.SetSizerAndFit(SZR_input)
    1683
    1684                  # layout page
    1685                  SZR_main = wx.BoxSizer(wx.VERTICAL)
    1686                  SZR_main.Add(PNL_form, 1, wx.EXPAND)
    1687                  self.SetSizer(SZR_main)
    1688          #--------------------------------------------------------
    1689 -        def set_identity(self, identity):
    1690                  return self.refresh(identity=identity)
    1691          #--------------------------------------------------------
    1692 -        def refresh(self, identity=None):
    1693                  if identity is not None:
    1694                          self.__ident = identity
    1695                  jobs = self.__ident.get_occupations()
    1696                  if len(jobs) > 0:
    1697                          self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
    1698                          self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
    1699                  return True
    1700          #--------------------------------------------------------
    1701 -        def save(self):
    1702                  if self.PRW_occupation.IsModified():
    1703                          new_job = self.PRW_occupation.GetValue().strip()
    1704                          jobs = self.__ident.get_occupations()
    1705                          for job in jobs:
    1706                                  if job['l10n_occupation'] == new_job:
    1707                                          continue
    1708                                  self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
    1709                          self.__ident.link_occupation(occupation = new_job)
    1710                  return True
    1711  #============================================================
    1712 -class cNotebookedPatEditionPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
    1713          """Patient demographics plugin for main notebook.
    1714
    1715          Hosts another notebook with pages for Identity, Contacts, etc.
    1716
    1717          Acts on/listens to the currently active patient.
    1718          """
    1719          #--------------------------------------------------------
    1720 -        def __init__(self, parent, id):
    1721                  wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
    1722                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1723                  self.__do_layout()
    1724                  self.__register_interests()
    1725          #--------------------------------------------------------
    1726          # public API
    1727          #--------------------------------------------------------
    1728          #--------------------------------------------------------
    1729          # internal helpers
    1730          #--------------------------------------------------------
    1731 -        def __do_layout(self):
    1732                  """Arrange widgets."""
    1733                  self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
    1734
    1735                  szr_main = wx.BoxSizer(wx.VERTICAL)
    1736                  szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
    1737                  self.SetSizerAndFit(szr_main)
    1738          #--------------------------------------------------------
    1739          # event handling
    1740          #--------------------------------------------------------
    1741 -        def __register_interests(self):
    1742                  gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
    1743                  gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
    1744          #--------------------------------------------------------
    1745 -        def _on_pre_patient_selection(self):
    1746                  self._schedule_data_reget()
    1747          #--------------------------------------------------------
    1748 -        def _on_post_patient_selection(self):
    1749                  self._schedule_data_reget()
    1750          # reget mixin API
    1751          #--------------------------------------------------------
    1752 -        def _populate_with_data(self):
    1753                  """Populate fields in pages with data from model."""
    1754                  pat = gmPerson.gmCurrentPatient()
    1755                  if pat.connected:
    1756                          self.__patient_notebook.identity = pat
    1757                  else:
    1758                          self.__patient_notebook.identity = None
    1759                  self.__patient_notebook.refresh()
    1760                  return True
    1761  #============================================================
    1762  #============================================================
    1763  if __name__ == "__main__":
    1764
    1765          #--------------------------------------------------------
    1766 -        def test_organizer_pnl():
    1767                  app = wx.PyWidgetTester(size = (600, 400))
    1768                  app.SetWidget(cKOrganizerSchedulePnl)
    1769                  app.MainLoop()
    1770          #--------------------------------------------------------
    1771 -        def test_person_names_pnl():
    1772                  app = wx.PyWidgetTester(size = (600, 400))
    1773                  widget = cPersonNamesManagerPnl(app.frame, -1)
    1774                  widget.identity = activate_patient()
    1775                  app.frame.Show(True)
    1776                  app.MainLoop()
    1777          #--------------------------------------------------------
    1778 -        def test_person_ids_pnl():
    1779                  app = wx.PyWidgetTester(size = (600, 400))
    1780                  widget = cPersonIDsManagerPnl(app.frame, -1)
    1781                  widget.identity = activate_patient()
    1782                  app.frame.Show(True)
    1783                  app.MainLoop()
    1784          #--------------------------------------------------------
    1785 -        def test_pat_ids_pnl():
    1786                  app = wx.PyWidgetTester(size = (600, 400))
    1787                  widget = cPersonIdentityManagerPnl(app.frame, -1)
    1788                  widget.identity = activate_patient()
    1789                  app.frame.Show(True)
    1790                  app.MainLoop()
    1791          #--------------------------------------------------------
    1792 -        def test_name_ea_pnl():
    1793                  app = wx.PyWidgetTester(size = (600, 400))
    1794                  app.SetWidget(cPersonNameEAPnl, name = activate_patient().get_active_name())
    1795                  app.MainLoop()
    1796          #--------------------------------------------------------
    1797 -        def test_cPersonDemographicsEditorNb():
    1798                  app = wx.PyWidgetTester(size = (600, 400))
    1799                  widget = cPersonDemographicsEditorNb(app.frame, -1)
    1800                  widget.identity = activate_patient()
    1801                  widget.refresh()
    1802                  app.frame.Show(True)
    1803                  app.MainLoop()
    1804          #--------------------------------------------------------
    1805 -        def activate_patient():
    1806                  patient = gmPersonSearch.ask_for_patient()
    1807                  if patient is None:
    1808                          print "No patient. Exiting gracefully..."
    1809                          sys.exit(0)
    1810                  from Gnumed.wxpython import gmPatSearchWidgets
    1811                  gmPatSearchWidgets.set_active_patient(patient=patient)
    1812                  return patient
    1813          #--------------------------------------------------------
    1814          if len(sys.argv) > 1 and sys.argv[1] == 'test':
    1815
    1816                  gmI18N.activate_locale()
    1817                  gmI18N.install_domain(domain='gnumed')
    1818                  gmPG2.get_connection()
    1819
    1820  #               app = wx.PyWidgetTester(size = (400, 300))
    1821  #               app.SetWidget(cNotebookedPatEditionPanel, -1)
    1822  #               app.frame.Show(True)
    1823  #               app.MainLoop()
    1824
    1825                  # phrasewheels
    1826  #               test_organizer_pnl()
    1827
    1828                  # identity related widgets
    1829                  #test_person_names_pnl()
    1830                  test_person_ids_pnl()
    1831                  #test_pat_ids_pnl()
    1832                  #test_name_ea_pnl()
    1833
    1834                  #test_cPersonDemographicsEditorNb()
    1835
    1836  #============================================================
    1837