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  #------------------------------------------------------------
     886 -def _validate_dob_field(dob_prw):
     887
     888          # valid timestamp ?
     889          if dob_prw.is_valid_timestamp(empty_is_valid = False):                  # properly colors the field
     890                  dob = dob_prw.date
     891                  # but year also usable ?
     892                  #if (dob.year > 1899) and (dob < gmDateTime.pydt_now_here()):
     893                  if (dob.year > pydt.MINYEAR) and (dob < gmDateTime.pydt_now_here()):
     894                          return True
     895
     896                  if dob.year < 1800:
     897                          msg = _(
     898                                  'DOB: %s\n'
     899                                  '\n'
     900                                  'While this is a valid point in time Python\n'
     901                                  'may not know how to deal with it.\n'
     902                                  '\n'
     903                                  'We suggest using January 1st 1801 instead and adding\n'
     904                                  'the true date of birth to the patient comment.\n'
     905                                  '\n'
     906                                  'Sorry for the inconvenience %s'
     907                          ) % (dob, gmTools.u_frowning_face)
     908                  else:
     909                          msg = _(
     910                                  'DOB: %s\n'
     911                                  '\n'
     912                                  'Date of birth in the future !'
     913                          ) % dob
     914                  gmGuiHelpers.gm_show_error (
     915                          msg,
     916                          _('Validating date of birth')
     917                  )
     918                  dob_prw.display_as_valid(False)
     919                  dob_prw.SetFocus()
     920                  return False
     921
     922          # invalid timestamp but not empty
     923          if dob_prw.GetValue().strip() != '':
     924                  dob_prw.display_as_valid(False)
     925                  gmDispatcher.send(signal = 'statustext', msg = _('Invalid date of birth.'))
     926                  dob_prw.SetFocus()
     927                  return False
     928
     929          # empty DOB field
     930          dob_prw.display_as_valid(False)
     931          return True
     932
     933  #------------------------------------------------------------
     934 -def _validate_tob_field(ctrl):
     935
     936          val = ctrl.GetValue().strip()
     937
     938          if val == '':
     939                  return True
     940
     941          converted, hours = gmTools.input2int(val[:2], 0, 23)
     942          if not converted:
     943                  return False
     944
     945          converted, minutes = gmTools.input2int(val[3:5], 0, 59)
     946          if not converted:
     947                  return False
     948
     949          return True
     950
     951  #------------------------------------------------------------
     952  from Gnumed.wxGladeWidgets import wxgIdentityEAPnl
     953
     954 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
     955          """An edit area for editing/creating title/gender/dob/dod etc."""
     956
     957 -        def __init__(self, *args, **kwargs):
     958
     959                  try:
     960                          data = kwargs['identity']
     961                          del kwargs['identity']
     962                  except KeyError:
     963                          data = None
     964
     965                  wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs)
     966                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     967
     968                  self.mode = 'new'
     969                  self.data = data
     970                  if data is not None:
     971                          self.mode = 'edit'
     972
     973  #               self.__init_ui()
     974          #----------------------------------------------------------------
     975  #       def __init_ui(self):
     976  #               # adjust phrasewheels etc
     977          #----------------------------------------------------------------
     978          # generic Edit Area mixin API
     979          #----------------------------------------------------------------
     980 -        def _valid_for_save(self):
     981
     982                  has_error = False
     983
     984                  if self._PRW_gender.GetData() is None:
     985                          self._PRW_gender.SetFocus()
     986                          has_error = True
     987
     988                  if self.data is not None:
     989                          if not _validate_dob_field(self._PRW_dob):
     990                                  has_error = True
     991
     992                  # TOB validation
     993                  if _validate_tob_field(self._TCTRL_tob):
     994                          self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True)
     995                  else:
     996                          has_error = True
     997                          self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False)
     998
     999                  if not self._PRW_dod.is_valid_timestamp(empty_is_valid = True):
    1000                          self.StatusText = _('Invalid date of death.')
    1001                          self._PRW_dod.SetFocus()
    1002                          has_error = True
    1003
    1004                  return (has_error is False)
    1005          #----------------------------------------------------------------
    1006 -        def _save_as_new(self):
    1007                  # not used yet
    1008                  return False
    1009          #----------------------------------------------------------------
    1010 -        def _save_as_update(self):
    1011
    1012                  if self._PRW_dob.GetValue().strip() == '':
    1013                          if not _empty_dob_allowed():
    1014                                  return False
    1015                          self.data['dob'] = None
    1016                  else:
    1017                          self.data['dob'] = self._PRW_dob.GetData()
    1018                  self.data['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue()
    1019                  val = self._TCTRL_tob.GetValue().strip()
    1020                  if val == '':
    1021                          self.data['tob'] = None
    1022                  else:
    1023                          self.data['tob'] = pydt.time(int(val[:2]), int(val[3:5]))
    1024                  self.data['gender'] = self._PRW_gender.GetData()
    1025                  self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), '')
    1026                  self.data['deceased'] = self._PRW_dod.GetData()
    1027                  self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
    1028
    1029                  self.data.save()
    1030                  return True
    1031          #----------------------------------------------------------------
    1032 -        def _refresh_as_new(self):
    1033                  pass
    1034          #----------------------------------------------------------------
    1035 -        def _refresh_from_existing(self):
    1036
    1037                  self._LBL_info.SetLabel('ID: #%s' % (
    1038                          self.data.ID
    1039                          # FIXME: add 'deleted' status
    1040                  ))
    1041                  if self.data['dob'] is None:
    1042                          val = ''
    1043                  else:
    1044                          val = gmDateTime.pydt_strftime (
    1045                                  self.data['dob'],
    1046                                  format = '%Y-%m-%d',
    1047                                  accuracy = gmDateTime.acc_minutes
    1048                          )
    1049                  self._PRW_dob.SetText(value = val, data = self.data['dob'])
    1050                  self._CHBOX_estimated_dob.SetValue(self.data['dob_is_estimated'])
    1051                  if self.data['tob'] is None:
    1052                          self._TCTRL_tob.SetValue('')
    1053                  else:
    1054                          self._TCTRL_tob.SetValue(self.data['tob'].strftime('%H:%M'))
    1055                  if self.data['deceased'] is None:
    1056                          val = ''
    1057                  else:
    1058                          val = gmDateTime.pydt_strftime (
    1059                                  self.data['deceased'],
    1060                                  format = '%Y-%m-%d %H:%M',
    1061                                  accuracy = gmDateTime.acc_minutes
    1062                          )
    1063                  self._PRW_dod.SetText(value = val, data = self.data['deceased'])
    1064                  self._PRW_gender.SetData(self.data['gender'])
    1065                  #self._PRW_ethnicity.SetValue()
    1066                  self._PRW_title.SetText(gmTools.coalesce(self.data['title'], ''))
    1067                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
    1068          #----------------------------------------------------------------
    1069 -        def _refresh_as_new_from_existing(self):
    1070                  pass
    1071  #------------------------------------------------------------
    1072  from Gnumed.wxGladeWidgets import wxgPersonNameEAPnl
    1073
    1074 -class cPersonNameEAPnl(wxgPersonNameEAPnl.wxgPersonNameEAPnl, gmEditArea.cGenericEditAreaMixin):
    1075          """An edit area for editing/creating names of people.
    1076
    1077          Does NOT act on/listen to the current patient.
    1078          """
    1079 -        def __init__(self, *args, **kwargs):
    1080
    1081                  try:
    1082                          data = kwargs['name']
    1083                          identity = gmPerson.cPerson(aPK_obj = data['pk_identity'])
    1084                          del kwargs['name']
    1085                  except KeyError:
    1086                          data = None
    1087                          identity = kwargs['identity']
    1088                          del kwargs['identity']
    1089
    1090                  wxgPersonNameEAPnl.wxgPersonNameEAPnl.__init__(self, *args, **kwargs)
    1091                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1092
    1093                  self.__identity = identity
    1094
    1095                  self.mode = 'new'
    1096                  self.data = data
    1097                  if data is not None:
    1098                          self.mode = 'edit'
    1099
    1100                  #self.__init_ui()
    1101          #----------------------------------------------------------------
    1102  #       def __init_ui(self):
    1103  #               # adjust phrasewheels etc
    1104          #----------------------------------------------------------------
    1105          # generic Edit Area mixin API
    1106          #----------------------------------------------------------------
    1107 -        def _valid_for_save(self):
    1108                  validity = True
    1109
    1110                  if self._PRW_lastname.GetValue().strip() == '':
    1111                          validity = False
    1112                          self._PRW_lastname.display_as_valid(False)
    1113                          self._PRW_lastname.SetFocus()
    1114                  else:
    1115                          self._PRW_lastname.display_as_valid(True)
    1116
    1117                  if self._PRW_firstname.GetValue().strip() == '':
    1118                          validity = False
    1119                          self._PRW_firstname.display_as_valid(False)
    1120                          self._PRW_firstname.SetFocus()
    1121                  else:
    1122                          self._PRW_firstname.display_as_valid(True)
    1123
    1124                  return validity
    1125          #----------------------------------------------------------------
    1126 -        def _save_as_new(self):
    1127
    1128                  first = self._PRW_firstname.GetValue().strip()
    1129                  last = self._PRW_lastname.GetValue().strip()
    1130                  active = self._CHBOX_active.GetValue()
    1131
    1132                  try:
    1133                          data = self.__identity.add_name(first, last, active)
    1134                  except gmPG2.dbapi.IntegrityError as exc:
    1135                          _log.exception('cannot save new name')
    1136                          gmGuiHelpers.gm_show_error (
    1137                                  aTitle = _('Adding name'),
    1138                                  aMessage = _(
    1139                                          'Cannot add this name to the patient !\n'
    1140                                          '\n'
    1141                                          ' %s'
    1142                                  ) % exc.pgerror
    1143                          )
    1144                          return False
    1145
    1146                  old_nick = self.__identity['active_name']['preferred']
    1147                  new_nick = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
    1148                  if active:
    1149                          data['preferred'] = gmTools.coalesce(new_nick, old_nick)
    1150                  else:
    1151                          data['preferred'] = new_nick
    1152                  data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
    1153                  data.save()
    1154
    1155                  self.data = data
    1156                  return True
    1157          #----------------------------------------------------------------
    1158 -        def _save_as_update(self):
    1159                  """The knack here is that we can only update a few fields.
    1160
    1161                  Otherwise we need to clone the name and update that.
    1162                  """
    1163                  first = self._PRW_firstname.GetValue().strip()
    1164                  last = self._PRW_lastname.GetValue().strip()
    1165                  active = self._CHBOX_active.GetValue()
    1166
    1167                  current_name = self.data['firstnames'].strip() + self.data['lastnames'].strip()
    1168                  new_name = first + last
    1169
    1170                  # editable fields only ?
    1171                  if new_name == current_name:
    1172                          self.data['active_name'] = self._CHBOX_active.GetValue()
    1173                          self.data['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
    1174                          self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
    1175                          self.data.save()
    1176                  # else clone name and update that
    1177                  else:
    1178                          try:
    1179                                  name = self.__identity.add_name(first, last, active)
    1180                          except gmPG2.dbapi.IntegrityError as exc:
    1181                                  _log.exception('cannot clone name when editing existing name')
    1182                                  gmGuiHelpers.gm_show_error (
    1183                                          aTitle = _('Editing name'),
    1184                                          aMessage = _(
    1185                                                  'Cannot clone a copy of this name !\n'
    1186                                                  '\n'
    1187                                                  ' %s'
    1188                                          ) % exc.pgerror
    1189                                  )
    1190                                  return False
    1191                          name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), '')
    1192                          name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '')
    1193                          name.save()
    1194                          self.data = name
    1195
    1196                  return True
    1197          #----------------------------------------------------------------
    1198 -        def _refresh_as_new(self):
    1199                  self._PRW_firstname.SetText(value = '', data = None)
    1200                  self._PRW_lastname.SetText(value = '', data = None)
    1201                  self._PRW_nick.SetText(value = '', data = None)
    1202                  self._TCTRL_comment.SetValue('')
    1203                  self._CHBOX_active.SetValue(False)
    1204
    1205                  self._PRW_firstname.SetFocus()
    1206          #----------------------------------------------------------------
    1207 -        def _refresh_as_new_from_existing(self):
    1208                  self._refresh_as_new()
    1209                  self._PRW_firstname.SetText(value = '', data = None)
    1210                  self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
    1211
    1212                  self._PRW_lastname.SetFocus()
    1213          #----------------------------------------------------------------
    1214 -        def _refresh_from_existing(self):
    1215                  self._PRW_firstname.SetText(self.data['firstnames'])
    1216                  self._PRW_lastname.SetText(self.data['lastnames'])
    1217                  self._PRW_nick.SetText(gmTools.coalesce(self.data['preferred'], ''))
    1218                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
    1219                  self._CHBOX_active.SetValue(self.data['active_name'])
    1220
    1221                  self._TCTRL_comment.SetFocus()
    1222  #------------------------------------------------------------
    1223  # list manager
    1224  #------------------------------------------------------------
    1225 -class cPersonNamesManagerPnl(gmListWidgets.cGenericListManagerPnl):
    1226          """A list for managing a person's names.
    1227
    1228          Does NOT act on/listen to the current patient.
    1229          """
    1230 -        def __init__(self, *args, **kwargs):
    1231
    1232                  try:
    1233                          self.__identity = kwargs['identity']
    1234                          del kwargs['identity']
    1235                  except KeyError:
    1236                          self.__identity = None
    1237
    1238                  gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
    1239
    1240                  self.refresh_callback = self.refresh
    1241                  self.new_callback = self._add_name
    1242                  self.edit_callback = self._edit_name
    1243                  self.delete_callback = self._del_name
    1244
    1245                  self.__init_ui()
    1246                  self.refresh()
    1247          #--------------------------------------------------------
    1248          # external API
    1249          #--------------------------------------------------------
    1250 -        def refresh(self, *args, **kwargs):
    1251                  if self.__identity is None:
    1252                          self._LCTRL_items.set_string_items()
    1253                          return
    1254
    1255                  names = self.__identity.get_names()
    1256                  self._LCTRL_items.set_string_items (
    1257                          items = [ [
    1258                                          gmTools.bool2str(n['active_name'], 'X', ''),
    1259                                          n['lastnames'],
    1260                                          n['firstnames'],
    1261                                          gmTools.coalesce(n['preferred'], ''),
    1262                                          gmTools.coalesce(n['comment'], '')
    1263                                  ] for n in names ]
    1264                  )
    1265                  self._LCTRL_items.set_column_widths()
    1266                  self._LCTRL_items.set_data(data = names)
    1267          #--------------------------------------------------------
    1268          # internal helpers
    1269          #--------------------------------------------------------
    1270 -        def __init_ui(self):
    1271                  self._LCTRL_items.set_columns(columns = [
    1272                          _('Active'),
    1273                          _('Lastname'),
    1274                          _('Firstname(s)'),
    1275                          _('Preferred Name'),
    1276                          _('Comment')
    1277                  ])
    1278          #--------------------------------------------------------
    1279 -        def _add_name(self):
    1280                  #ea = cPersonNameEAPnl(self, -1, name = self.__identity.get_active_name())
    1281                  ea = cPersonNameEAPnl(self, -1, identity = self.__identity)
    1282                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1283                  dlg.SetTitle(_('Adding new name'))
    1284                  if dlg.ShowModal() == wx.ID_OK:
    1285                          dlg.DestroyLater()
    1286                          return True
    1287                  dlg.DestroyLater()
    1288                  return False
    1289          #--------------------------------------------------------
    1290 -        def _edit_name(self, name):
    1291                  ea = cPersonNameEAPnl(self, -1, name = name)
    1292                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1293                  dlg.SetTitle(_('Editing name'))
    1294                  if dlg.ShowModal() == wx.ID_OK:
    1295                          dlg.DestroyLater()
    1296                          return True
    1297                  dlg.DestroyLater()
    1298                  return False
    1299          #--------------------------------------------------------
    1300 -        def _del_name(self, name):
    1301
    1302                  if len(self.__identity.get_names()) == 1:
    1303                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the only name of a person.'), beep = True)
    1304                          return False
    1305
    1306                  if name['active_name']:
    1307                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete the active name of a person.'), beep = True)
    1308                          return False
    1309
    1310                  go_ahead = gmGuiHelpers.gm_show_question (
    1311                          _(      'It is often advisable to keep old names around and\n'
    1312                                  'just create a new "currently active" name.\n'
    1313                                  '\n'
    1314                                  'This allows finding the patient by both the old\n'
    1315                                  'and the new name (think before/after marriage).\n'
    1316                                  '\n'
    1317                                  'Do you still want to really delete\n'
    1318                                  "this name from the patient ?"
    1319                          ),
    1320                          _('Deleting name')
    1321                  )
    1322                  if not go_ahead:
    1323                          return False
    1324
    1325                  self.__identity.delete_name(name = name)
    1326                  return True
    1327          #--------------------------------------------------------
    1328          # properties
    1329          #--------------------------------------------------------
    1330 -        def _get_identity(self):
    1331                  return self.__identity
    1332
    1333 -        def _set_identity(self, identity):
    1334                  self.__identity = identity
    1335                  self.refresh()
    1336
    1337          identity = property(_get_identity, _set_identity)
    1338
    1339  #------------------------------------------------------------
    1340 -class cPersonIDsManagerPnl(gmListWidgets.cGenericListManagerPnl):
    1341          """A list for managing a person's external IDs.
    1342
    1343          Does NOT act on/listen to the current patient.
    1344          """
    1345 -        def __init__(self, *args, **kwargs):
    1346
    1347                  try:
    1348                          self.__identity = kwargs['identity']
    1349                          del kwargs['identity']
    1350                  except KeyError:
    1351                          self.__identity = None
    1352
    1353                  gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs)
    1354
    1355                  self.refresh_callback = self.refresh
    1356                  self.new_callback = self._add_id
    1357                  self.edit_callback = self._edit_id
    1358                  self.delete_callback = self._del_id
    1359
    1360                  self.__init_ui()
    1361                  self.refresh()
    1362          #--------------------------------------------------------
    1363          # external API
    1364          #--------------------------------------------------------
    1365 -        def refresh(self, *args, **kwargs):
    1366                  if self.__identity is None:
    1367                          self._LCTRL_items.set_string_items()
    1368                          return
    1369
    1370                  ids = self.__identity.get_external_ids()
    1371                  self._LCTRL_items.set_string_items (
    1372                          items = [ [
    1373                                          i['name'],
    1374                                          i['value'],
    1375                                          gmTools.coalesce(i['issuer'], ''),
    1376                                          gmTools.coalesce(i['comment'], '')
    1377                                  ] for i in ids
    1378                          ]
    1379                  )
    1380                  self._LCTRL_items.set_column_widths()
    1381                  self._LCTRL_items.set_data(data = ids)
    1382          #--------------------------------------------------------
    1383          # internal helpers
    1384          #--------------------------------------------------------
    1385 -        def __init_ui(self):
    1386                  self._LCTRL_items.set_columns(columns = [
    1387                          _('ID type'),
    1388                          _('Value'),
    1389                          _('Issuer'),
    1390                          _('Comment')
    1391                  ])
    1392          #--------------------------------------------------------
    1393 -        def _add_id(self):
    1394                  ea = cExternalIDEditAreaPnl(self, -1)
    1395                  ea.id_holder = self.__identity
    1396                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea)
    1397                  dlg.SetTitle(_('Adding new external ID'))
    1398                  if dlg.ShowModal() == wx.ID_OK:
    1399                          dlg.DestroyLater()
    1400                          return True
    1401                  dlg.DestroyLater()
    1402                  return False
    1403          #--------------------------------------------------------
    1404 -        def _edit_id(self, ext_id):
    1405                  ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id)
    1406                  ea.id_holder = self.__identity
    1407                  dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True)
    1408                  dlg.SetTitle(_('Editing external ID'))
    1409                  if dlg.ShowModal() == wx.ID_OK:
    1410                          dlg.DestroyLater()
    1411                          return True
    1412                  dlg.DestroyLater()
    1413                  return False
    1414          #--------------------------------------------------------
    1415 -        def _del_id(self, ext_id):
    1416                  go_ahead = gmGuiHelpers.gm_show_question (
    1417                          _(      'Do you really want to delete this\n'
    1418                                  'external ID from the patient ?'),
    1419                          _('Deleting external ID')
    1420                  )
    1421                  if not go_ahead:
    1422                          return False
    1423                  self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id'])
    1424                  return True
    1425          #--------------------------------------------------------
    1426          # properties
    1427          #--------------------------------------------------------
    1428 -        def _get_identity(self):
    1429                  return self.__identity
    1430
    1431 -        def _set_identity(self, identity):
    1432                  self.__identity = identity
    1433                  self.refresh()
    1434
    1435          identity = property(_get_identity, _set_identity)
    1436  #------------------------------------------------------------
    1437  # integrated panels
    1438  #------------------------------------------------------------
    1439  from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl
    1440
    1441 -class cPersonIdentityManagerPnl(wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl):
    1442          """A panel for editing identity data for a person.
    1443
    1444          - provides access to:
    1445            - identity EA
    1446            - name list manager
    1447            - external IDs list manager
    1448
    1449          Does NOT act on/listen to the current patient.
    1450          """
    1451 -        def __init__(self, *args, **kwargs):
    1452
    1453                  wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs)
    1454
    1455                  self.__identity = None
    1456                  self.refresh()
    1457          #--------------------------------------------------------
    1458          # external API
    1459          #--------------------------------------------------------
    1460 -        def refresh(self):
    1461                  self._PNL_names.identity = self.__identity
    1462                  self._PNL_ids.identity = self.__identity
    1463                  # this is an Edit Area:
    1464                  self._PNL_identity.mode = 'new'
    1465                  self._PNL_identity.data = self.__identity
    1466                  if self.__identity is not None:
    1467                          self._PNL_identity.mode = 'edit'
    1468                          self._PNL_identity._refresh_from_existing()
    1469          #--------------------------------------------------------
    1470          # properties
    1471          #--------------------------------------------------------
    1472 -        def _get_identity(self):
    1473                  return self.__identity
    1474
    1475 -        def _set_identity(self, identity):
    1476                  self.__identity = identity
    1477                  self.refresh()
    1478
    1479          identity = property(_get_identity, _set_identity)
    1480          #--------------------------------------------------------
    1481          # event handlers
    1482          #--------------------------------------------------------
    1483 -        def _on_save_identity_details_button_pressed(self, event):
    1484                  if not self._PNL_identity.save():
    1485                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot save identity. Incomplete information.'), beep = True)
    1486                  #self._PNL_identity.refresh()
    1487          #--------------------------------------------------------
    1488 -        def _on_reload_identity_button_pressed(self, event):
    1489                  self._PNL_identity.refresh()
    1490
    1491  #============================================================
    1492  from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl
    1493
    1494 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
    1495 -        def __init__(self, *args, **kwargs):
    1496
    1497                  wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs)
    1498
    1499                  self.__identity = None
    1500                  self._PRW_provider.selection_only = False
    1501                  self.refresh()
    1502          #--------------------------------------------------------
    1503          # external API
    1504          #--------------------------------------------------------
    1505 -        def refresh(self):
    1506
    1507                  tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.')
    1508
    1509                  if self.__identity is None:
    1510                          self._TCTRL_er_contact.SetValue('')
    1511                          self._TCTRL_person.person = None
    1512                          self._TCTRL_person.SetToolTip(tt)
    1513
    1514                          self._PRW_provider.SetText(value = '', data = None)
    1515                          return
    1516
    1517                  self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], ''))
    1518                  if self.__identity['pk_emergency_contact'] is not None:
    1519                          ident = gmPerson.cPerson(aPK_obj = self.__identity['pk_emergency_contact'])
    1520                          self._TCTRL_person.person = ident
    1521                          tt = '%s\n\n%s\n\n%s' % (
    1522                                  tt,
    1523                                  ident['description_gender'],
    1524                                  '\n'.join([
    1525                                          '%s: %s%s' % (
    1526                                                  c['l10n_comm_type'],
    1527                                                  c['url'],
    1528                                                  gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), '', '')
    1529                                          )
    1530                                          for c in ident.get_comm_channels()
    1531                                  ])
    1532                          )
    1533                  else:
    1534                          self._TCTRL_person.person = None
    1535
    1536                  self._TCTRL_person.SetToolTip(tt)
    1537
    1538                  if self.__identity['pk_primary_provider'] is None:
    1539                          self._PRW_provider.SetText(value = '', data = None)
    1540                  else:
    1541                          self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
    1542
    1543                  self._PNL_external_care.identity = self.__identity
    1544          #--------------------------------------------------------
    1545          # properties
    1546          #--------------------------------------------------------
    1547 -        def _get_identity(self):
    1548                  return self.__identity
    1549
    1550 -        def _set_identity(self, identity):
    1551                  self.__identity = identity
    1552                  self.refresh()
    1553
    1554          identity = property(_get_identity, _set_identity)
    1555          #--------------------------------------------------------
    1556          # event handlers
    1557          #--------------------------------------------------------
    1558 -        def _on_save_button_pressed(self, event):
    1559                  if self.__identity is not None:
    1560                          self.__identity['emergency_contact'] = self._TCTRL_er_contact.GetValue().strip()
    1561                          if self._TCTRL_person.person is not None:
    1562                                  self.__identity['pk_emergency_contact'] = self._TCTRL_person.person.ID
    1563                          if self._PRW_provider.GetValue().strip == '':
    1564                                  self.__identity['pk_primary_provider'] = None
    1565                          else:
    1566                                  self.__identity['pk_primary_provider'] = self._PRW_provider.GetData()
    1567
    1568                          self.__identity.save()
    1569                          gmDispatcher.send(signal = 'statustext', msg = _('Emergency data and primary provider saved.'), beep = False)
    1570
    1571                  event.Skip()
    1572          #--------------------------------------------------------
    1573 -        def _on_reload_button_pressed(self, event):
    1574                  self.refresh()
    1575          #--------------------------------------------------------
    1576 -        def _on_remove_contact_button_pressed(self, event):
    1577                  event.Skip()
    1578
    1579                  if self.__identity is None:
    1580                          return
    1581
    1582                  self._TCTRL_person.person = None
    1583
    1584                  self.__identity['pk_emergency_contact'] = None
    1585                  self.__identity.save()
    1586          #--------------------------------------------------------
    1587 -        def _on_button_activate_contact_pressed(self, event):
    1588                  ident = self._TCTRL_person.person
    1589                  if ident is not None:
    1590                          from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
    1591                          wx.CallAfter(set_active_patient, patient = ident)
    1592
    1593                  event.Skip()
    1594
    1595  #============================================================
    1596  # patient demographics editing classes
    1597  #============================================================
    1598 -class cPersonDemographicsEditorNb(wx.Notebook):
    1599          """Notebook displaying demographics editing pages:
    1600
    1601                  - Identity (as per Jim/Rogerio 12/2011)
    1602                  - Contacts (addresses, phone numbers, etc)
    1603                  - Social network (significant others, GP, etc)
    1604
    1605          Does NOT act on/listen to the current patient.
    1606          """
    1607          #--------------------------------------------------------
    1608 -        def __init__(self, parent, id):
    1609
    1610                  wx.Notebook.__init__ (
    1611                          self,
    1612                          parent = parent,
    1613                          id = id,
    1614                          style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
    1615                          name = self.__class__.__name__
    1616                  )
    1617                  _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
    1618
    1619                  self.__identity = None
    1620                  self.__do_layout()
    1621                  self.SetSelection(0)
    1622          #--------------------------------------------------------
    1623          # public API
    1624          #--------------------------------------------------------
    1625 -        def refresh(self):
    1626                  """Populate fields in pages with data from model."""
    1627                  for page_idx in range(self.GetPageCount()):
    1628                          page = self.GetPage(page_idx)
    1629                          page.identity = self.__identity
    1630
    1631                  return True
    1632          #--------------------------------------------------------
    1633          # internal API
    1634          #--------------------------------------------------------
    1635 -        def __do_layout(self):
    1636                  """Build patient edition notebook pages."""
    1637
    1638                  # identity page
    1639                  new_page = cPersonIdentityManagerPnl(self, -1)
    1640                  new_page.identity = self.__identity
    1641                  self.AddPage (
    1642                          page = new_page,
    1643                          text = _('Identity'),
    1644                          select = False
    1645                  )
    1646
    1647                  # contacts page
    1648                  new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1)
    1649                  new_page.identity = self.__identity
    1650                  self.AddPage (
    1651                          page = new_page,
    1652                          text = _('Contacts'),
    1653                          select = True
    1654                  )
    1655
    1656                  # social network page
    1657                  new_page = cPersonSocialNetworkManagerPnl(self, -1)
    1658                  new_page.identity = self.__identity
    1659                  self.AddPage (
    1660                          page = new_page,
    1661                          text = _('Social network'),
    1662                          select = False
    1663                  )
    1664          #--------------------------------------------------------
    1665          # properties
    1666          #--------------------------------------------------------
    1667 -        def _get_identity(self):
    1668                  return self.__identity
    1669
    1670 -        def _set_identity(self, identity):
    1671                  self.__identity = identity
    1672
    1673          identity = property(_get_identity, _set_identity)
    1674
    1675  #============================================================
    1676  # old occupation widgets
    1677  #============================================================
    1678  # FIXME: support multiple occupations
    1679  # FIXME: redo with wxGlade
    1680
    1681 -class cPatOccupationsPanel(wx.Panel):
    1682          """Page containing patient occupations edition fields.
    1683          """
    1684 -        def __init__(self, parent, id, ident=None):
    1685                  """
    1686                  Creates a new instance of BasicPatDetailsPage
    1687                  @param parent - The parent widget
    1688                  @type parent - A wx.Window instance
    1689                  @param id - The widget id
    1690                  @type id - An integer
    1691                  """
    1692                  wx.Panel.__init__(self, parent, id)
    1693                  self.__ident = ident
    1694                  self.__do_layout()
    1695          #--------------------------------------------------------
    1696 -        def __do_layout(self):
    1697                  PNL_form = wx.Panel(self, -1)
    1698                  # occupation
    1699                  STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation'))
    1700                  self.PRW_occupation = cOccupationPhraseWheel(PNL_form, -1)
    1701                  self.PRW_occupation.SetToolTip(_("primary occupation of the patient"))
    1702                  # known since
    1703                  STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated'))
    1704                  self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY)
    1705
    1706                  # layout input widgets
    1707                  SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4)
    1708                  SZR_input.AddGrowableCol(1)
    1709                  SZR_input.Add(STT_occupation, 0, wx.SHAPED)
    1710                  SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND)
    1711                  SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED)
    1712                  SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND)
    1713                  PNL_form.SetSizerAndFit(SZR_input)
    1714
    1715                  # layout page
    1716                  SZR_main = wx.BoxSizer(wx.VERTICAL)
    1717                  SZR_main.Add(PNL_form, 1, wx.EXPAND)
    1718                  self.SetSizer(SZR_main)
    1719          #--------------------------------------------------------
    1720 -        def set_identity(self, identity):
    1721                  return self.refresh(identity=identity)
    1722          #--------------------------------------------------------
    1723 -        def refresh(self, identity=None):
    1724                  if identity is not None:
    1725                          self.__ident = identity
    1726                  jobs = self.__ident.get_occupations()
    1727                  if len(jobs) > 0:
    1728                          self.PRW_occupation.SetText(jobs[0]['l10n_occupation'])
    1729                          self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y'))
    1730                  return True
    1731          #--------------------------------------------------------
    1732 -        def save(self):
    1733                  if self.PRW_occupation.IsModified():
    1734                          new_job = self.PRW_occupation.GetValue().strip()
    1735                          jobs = self.__ident.get_occupations()
    1736                          for job in jobs:
    1737                                  if job['l10n_occupation'] == new_job:
    1738                                          continue
    1739                                  self.__ident.unlink_occupation(occupation = job['l10n_occupation'])
    1740                          self.__ident.link_occupation(occupation = new_job)
    1741                  return True
    1742
    1743  #============================================================
    1744 -class cNotebookedPatEditionPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
    1745          """Patient demographics plugin for main notebook.
    1746
    1747          Hosts another notebook with pages for Identity, Contacts, etc.
    1748
    1749          Acts on/listens to the currently active patient.
    1750          """
    1751          #--------------------------------------------------------
    1752 -        def __init__(self, parent, id):
    1753                  wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER)
    1754                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1755                  self.__do_layout()
    1756                  self.__register_interests()
    1757          #--------------------------------------------------------
    1758          # public API
    1759          #--------------------------------------------------------
    1760          #--------------------------------------------------------
    1761          # internal helpers
    1762          #--------------------------------------------------------
    1763 -        def __do_layout(self):
    1764                  """Arrange widgets."""
    1765                  self.__patient_notebook = cPersonDemographicsEditorNb(self, -1)
    1766
    1767                  szr_main = wx.BoxSizer(wx.VERTICAL)
    1768                  szr_main.Add(self.__patient_notebook, 1, wx.EXPAND)
    1769                  self.SetSizerAndFit(szr_main)
    1770          #--------------------------------------------------------
    1771          # event handling
    1772          #--------------------------------------------------------
    1773 -        def __register_interests(self):
    1774                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
    1775                  gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
    1776          #--------------------------------------------------------
    1777 -        def _on_pre_patient_unselection(self):
    1778                  self.__patient_notebook.identity = None
    1779                  self.__patient_notebook.refresh()
    1780          #--------------------------------------------------------
    1781 -        def _on_post_patient_selection(self):
    1782                  self._schedule_data_reget()
    1783          #--------------------------------------------------------
    1784          # reget mixin API
    1785          #--------------------------------------------------------
    1786 -        def _populate_with_data(self):
    1787                  """Populate fields in pages with data from model."""
    1788                  pat = gmPerson.gmCurrentPatient()
    1789                  if pat.connected:
    1790                          self.__patient_notebook.identity = pat
    1791                  else:
    1792                          self.__patient_notebook.identity = None
    1793                  self.__patient_notebook.refresh()
    1794                  return True
    1795
    1796  #============================================================
    1797  #============================================================
    1798  if __name__ == "__main__":
    1799
    1800          #--------------------------------------------------------
    1801 -        def test_organizer_pnl():
    1802                  app = wx.PyWidgetTester(size = (600, 400))
    1803                  app.SetWidget(cKOrganizerSchedulePnl)
    1804                  app.MainLoop()
    1805          #--------------------------------------------------------
    1806 -        def test_person_names_pnl():
    1807                  app = wx.PyWidgetTester(size = (600, 400))
    1808                  widget = cPersonNamesManagerPnl(app.frame, -1)
    1809                  widget.identity = activate_patient()
    1810                  app.frame.Show(True)
    1811                  app.MainLoop()
    1812          #--------------------------------------------------------
    1813 -        def test_person_ids_pnl():
    1814                  app = wx.PyWidgetTester(size = (600, 400))
    1815                  widget = cPersonIDsManagerPnl(app.frame, -1)
    1816                  widget.identity = activate_patient()
    1817                  app.frame.Show(True)
    1818                  app.MainLoop()
    1819          #--------------------------------------------------------
    1820 -        def test_pat_ids_pnl():
    1821                  app = wx.PyWidgetTester(size = (600, 400))
    1822                  widget = cPersonIdentityManagerPnl(app.frame, -1)
    1823                  widget.identity = activate_patient()
    1824                  app.frame.Show(True)
    1825                  app.MainLoop()
    1826          #--------------------------------------------------------
    1827 -        def test_name_ea_pnl():
    1828                  app = wx.PyWidgetTester(size = (600, 400))
    1829                  app.SetWidget(cPersonNameEAPnl, name = activate_patient().get_active_name())
    1830                  app.MainLoop()
    1831          #--------------------------------------------------------
    1832 -        def test_cPersonDemographicsEditorNb():
    1833                  app = wx.PyWidgetTester(size = (600, 400))
    1834                  widget = cPersonDemographicsEditorNb(app.frame, -1)
    1835                  widget.identity = activate_patient()
    1836                  widget.refresh()
    1837                  app.frame.Show(True)
    1838                  app.MainLoop()
    1839          #--------------------------------------------------------
    1840 -        def activate_patient():
    1841                  patient = gmPersonSearch.ask_for_patient()
    1842                  if patient is None:
    1843                          print("No patient. Exiting gracefully...")
    1844                          sys.exit(0)
    1845                  from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
    1846                  set_active_patient(patient = patient)
    1847                  return patient
    1848          #--------------------------------------------------------
    1849          if len(sys.argv) > 1 and sys.argv[1] == 'test':
    1850
    1851                  gmI18N.activate_locale()
    1852                  gmI18N.install_domain(domain='gnumed')
    1853                  gmPG2.get_connection()
    1854
    1855  #               app = wx.PyWidgetTester(size = (400, 300))
    1856  #               app.SetWidget(cNotebookedPatEditionPanel, -1)
    1857  #               app.frame.Show(True)
    1858  #               app.MainLoop()
    1859
    1860                  # phrasewheels
    1861  #               test_organizer_pnl()
    1862
    1863                  # identity related widgets
    1864                  #test_person_names_pnl()
    1865                  test_person_ids_pnl()
    1866                  #test_pat_ids_pnl()
    1867                  #test_name_ea_pnl()
    1868
    1869                  #test_cPersonDemographicsEditorNb()
    1870
    1871  #============================================================
    1872