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