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

# Source Code for Module Gnumed.wxpython.gmEMRStructWidgets

       1  """GNUmed EMR structure editors
       2
       3          This module contains widgets to create and edit EMR structural
       4          elements (issues, enconters, episodes).
       5
       6          This is based on initial work and ideas by Syan <kittylitter@swiftdsl.com.au>
       7          and Karsten <Karsten.Hilbert@gmx.net>.
       8  """
       9  #================================================================
      10  __version__ = "$Revision: 1.114 $"
      11  __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
      12  __license__ = "GPL"
      13
      14  # stdlib
      15  import sys, re, datetime as pydt, logging, time
      16
      17
      18  # 3rd party
      19  import wx
      20
      21
      22  # GNUmed
      23  if __name__ == '__main__':
      24          sys.path.insert(0, '../../')
      25  from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
      26  from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery, gmPersonSearch
      27  from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
      28  from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
      29  from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
      30
      31
      32  _log = logging.getLogger('gm.ui')
      33  _log.info(__version__)
      34  #================================================================
      35  # performed procedure related widgets/functions
      36  #----------------------------------------------------------------
      37 -def manage_performed_procedures(parent=None):
      38
      39          pat = gmPerson.gmCurrentPatient()
      40          emr = pat.get_emr()
      41
      42          if parent is None:
      43                  parent = wx.GetApp().GetTopWindow()
      44          #-----------------------------------------
      45          def edit(procedure=None):
      46                  return edit_procedure(parent = parent, procedure = procedure)
      47          #-----------------------------------------
      48          def delete(procedure=None):
      49                  if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
      50                          return True
      51
      52                  gmDispatcher.send (
      53                          signal = u'statustext',
      54                          msg = _('Cannot delete performed procedure.'),
      55                          beep = True
      56                  )
      57                  return False
      58          #-----------------------------------------
      59          def refresh(lctrl):
      60                  procs = emr.get_performed_procedures()
      61
      62                  items = [
      63                          [
      64                                  u'%s%s' % (
      65                                          p['clin_when'].strftime('%Y-%m-%d'),
      66                                          gmTools.bool2subst (
      67                                                  p['is_ongoing'],
      68                                                  _(' (ongoing)'),
      69                                                  gmTools.coalesce (
      70                                                          initial = p['clin_end'],
      71                                                          instead = u'',
      72                                                          template_initial = u' - %s',
      73                                                          function_initial = ('strftime', u'%Y-%m-%d')
      74                                                  )
      75                                          )
      76                                  ),
      77                                  p['clin_where'],
      78                                  p['episode'],
      79                                  p['performed_procedure']
      80                          ] for p in procs
      81                  ]
      82                  lctrl.set_string_items(items = items)
      83                  lctrl.set_data(data = procs)
      84          #-----------------------------------------
      85          gmListWidgets.get_choices_from_list (
      86                  parent = parent,
      87                  msg = _('\nSelect the procedure you want to edit !\n'),
      88                  caption = _('Editing performed procedures ...'),
      89                  columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
      90                  single_selection = True,
      91                  edit_callback = edit,
      92                  new_callback = edit,
      93                  delete_callback = delete,
      94                  refresh_callback = refresh
      95          )
      96  #----------------------------------------------------------------
      97 -def edit_procedure(parent=None, procedure=None):
      98          ea = cProcedureEAPnl(parent = parent, id = -1)
      99          ea.data = procedure
     100          ea.mode = gmTools.coalesce(procedure, 'new', 'edit')
     101          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
     102          dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure')))
     103          if dlg.ShowModal() == wx.ID_OK:
     104                  dlg.Destroy()
     105                  return True
     106          dlg.Destroy()
     107          return False
     108  #----------------------------------------------------------------
     109  from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
     110
     111 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
     112
     113 -        def __init__(self, *args, **kwargs):
     114                  wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs)
     115                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     116
     117                  self.mode = 'new'
     118                  self.data = None
     119
     120                  self.__init_ui()
     121          #----------------------------------------------------------------
     122 -        def __init_ui(self):
     123                  self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
     124                  self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
     125                  self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
     126                  self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
     127                  self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
     128
     129                  # location
     130                  mp = gmMatchProvider.cMatchProvider_SQL2 (
     131                          queries = [
     132  u"""
     133  SELECT DISTINCT ON (data) data, location
     134  FROM (
     135          SELECT
     136                  clin_where as data,
     137                  clin_where as location
     138          FROM
     139                  clin.procedure
     140          WHERE
     141                  clin_where %(fragment_condition)s
     142
     143                  UNION ALL
     144
     145          SELECT
     146                  narrative as data,
     147                  narrative as location
     148          FROM
     149                  clin.hospital_stay
     150          WHERE
     151                  narrative %(fragment_condition)s
     152  ) as union_result
     153  ORDER BY data
     154  LIMIT 25"""
     155                          ]
     156                  )
     157                  mp.setThresholds(2, 4, 6)
     158                  self._PRW_location.matcher = mp
     159
     160                  # procedure
     161                  mp = gmMatchProvider.cMatchProvider_SQL2 (
     162                          queries = [
     163  u"""
     164  select distinct on (narrative) narrative, narrative
     165  from clin.procedure
     166  where narrative %(fragment_condition)s
     167  order by narrative
     168  limit 25
     169  """                     ]
     170                  )
     171                  mp.setThresholds(2, 4, 6)
     172                  self._PRW_procedure.matcher = mp
     173          #----------------------------------------------------------------
     174 -        def _on_hospital_stay_lost_focus(self):
     175                  stay = self._PRW_hospital_stay.GetData()
     176                  if stay is None:
     177                          self._PRW_hospital_stay.SetText()
     178                          self._PRW_location.Enable(True)
     179                          self._PRW_episode.Enable(True)
     180                          self._LBL_hospital_details.SetLabel(u'')
     181                  else:
     182                          self._PRW_location.SetText()
     183                          self._PRW_location.Enable(False)
     184                          self._PRW_episode.SetText()
     185                          self._PRW_episode.Enable(False)
     186                          self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
     187          #----------------------------------------------------------------
     188 -        def _on_location_lost_focus(self):
     189                  if self._PRW_location.GetValue().strip() == u'':
     190                          self._PRW_hospital_stay.Enable(True)
     191  #                       self._PRW_episode.Enable(False)
     192                  else:
     193                          self._PRW_hospital_stay.SetText()
     194                          self._PRW_hospital_stay.Enable(False)
     195                          self._PRW_hospital_stay.display_as_valid(True)
     196  #                       self._PRW_episode.Enable(True)
     197          #----------------------------------------------------------------
     198 -        def _on_start_lost_focus(self):
     199                  if not self._DPRW_date.is_valid_timestamp():
     200                          return
     201                  end = self._DPRW_end.GetData()
     202                  if end is None:
     203                          return
     204                  end = end.get_pydt()
     205                  start = self._DPRW_date.GetData().get_pydt()
     206                  if start < end:
     207                          return
     208                  self._DPRW_date.display_as_valid(False)
     209          #----------------------------------------------------------------
     210 -        def _on_end_lost_focus(self):
     211                  end = self._DPRW_end.GetData()
     212                  if end is None:
     213                          self._CHBOX_ongoing.Enable(True)
     214                          self._DPRW_end.display_as_valid(True)
     215                  else:
     216                          self._CHBOX_ongoing.Enable(False)
     217                          end = end.get_pydt()
     218                          now = gmDateTime.pydt_now_here()
     219                          if end > now:
     220                                  self._CHBOX_ongoing.SetValue(True)
     221                          else:
     222                                  self._CHBOX_ongoing.SetValue(False)
     223                          start = self._DPRW_date.GetData()
     224                          if start is None:
     225                                  self._DPRW_end.display_as_valid(True)
     226                          else:
     227                                  start = start.get_pydt()
     228                                  if end > start:
     229                                          self._DPRW_end.display_as_valid(True)
     230                                  else:
     231                                          self._DPRW_end.display_as_valid(False)
     232          #----------------------------------------------------------------
     233          # generic Edit Area mixin API
     234          #----------------------------------------------------------------
     235 -        def _valid_for_save(self):
     236
     237                  has_errors = False
     238
     239                  if not self._DPRW_date.is_valid_timestamp():
     240                          self._DPRW_date.display_as_valid(False)
     241                          has_errors = True
     242                  else:
     243                          self._DPRW_date.display_as_valid(True)
     244
     245                  end = self._DPRW_end.GetData()
     246                  self._DPRW_end.display_as_valid(True)
     247                  if end is not None:
     248                          end = end.get_pydt()
     249                          start = self._DPRW_end.GetData()
     250                          if start is not None:
     251                                  start = start.get_pydt()
     252                                  if end < start:
     253                                          has_errors = True
     254                                          self._DPRW_end.display_as_valid(False)
     255                          if self._CHBOX_ongoing.IsChecked():
     256                                  now = gmDateTime.pydt_now_here()
     257                                  if end < now:
     258                                          has_errors = True
     259                                          self._DPRW_end.display_as_valid(False)
     260
     261                  if self._PRW_hospital_stay.GetData() is None:
     262                          if self._PRW_episode.GetData() is None:
     263                                  self._PRW_episode.display_as_valid(False)
     264                                  has_errors = True
     265                          else:
     266                                  self._PRW_episode.display_as_valid(True)
     267                  else:
     268                          self._PRW_episode.display_as_valid(True)
     269
     270                  if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''):
     271                          self._PRW_procedure.display_as_valid(False)
     272                          has_errors = True
     273                  else:
     274                          self._PRW_procedure.display_as_valid(True)
     275
     276                  invalid_location = (
     277                          (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'')
     278                                  or
     279                          (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'')
     280                  )
     281                  if invalid_location:
     282                          self._PRW_hospital_stay.display_as_valid(False)
     283                          self._PRW_location.display_as_valid(False)
     284                          has_errors = True
     285                  else:
     286                          self._PRW_hospital_stay.display_as_valid(True)
     287                          self._PRW_location.display_as_valid(True)
     288
     289                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot save procedure.'), beep = True)
     290
     291                  return (has_errors is False)
     292          #----------------------------------------------------------------
     293 -        def _save_as_new(self):
     294
     295                  pat = gmPerson.gmCurrentPatient()
     296                  emr = pat.get_emr()
     297
     298                  if self._PRW_hospital_stay.GetData() is None:
     299                          stay = None
     300                          epi = self._PRW_episode.GetData()
     301                          loc = self._PRW_location.GetValue().strip()
     302                  else:
     303                          stay = self._PRW_hospital_stay.GetData()
     304                          epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode']
     305                          loc = None
     306
     307                  proc = emr.add_performed_procedure (
     308                          episode = epi,
     309                          location = loc,
     310                          hospital_stay = stay,
     311                          procedure = self._PRW_procedure.GetValue().strip()
     312                  )
     313
     314                  proc['clin_when'] = self._DPRW_date.GetData().get_pydt()
     315                  if self._DPRW_end.GetData() is None:
     316                          proc['clin_end'] = None
     317                  else:
     318                          proc['clin_end'] = self._DPRW_end.GetData().get_pydt()
     319                  proc['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
     320                  proc.save()
     321
     322                  proc.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
     323
     324                  self.data = proc
     325
     326                  return True
     327          #----------------------------------------------------------------
     328 -        def _save_as_update(self):
     329                  self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
     330
     331                  if self._DPRW_end.GetData() is None:
     332                          self.data['clin_end'] = None
     333                  else:
     334                          self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
     335
     336                  self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
     337
     338                  if self._PRW_hospital_stay.GetData() is None:
     339                          self.data['pk_hospital_stay'] = None
     340                          self.data['clin_where'] = self._PRW_location.GetValue().strip()
     341                          self.data['pk_episode'] = self._PRW_episode.GetData()
     342                  else:
     343                          self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
     344                          self.data['clin_where'] = None
     345                          stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
     346                          self.data['pk_episode'] = stay['pk_episode']
     347
     348                  self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
     349
     350                  self.data.save()
     351                  self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
     352
     353                  return True
     354          #----------------------------------------------------------------
     355 -        def _refresh_as_new(self):
     356                  self._DPRW_date.SetText()
     357                  self._DPRW_end.SetText()
     358                  self._CHBOX_ongoing.SetValue(False)
     359                  self._CHBOX_ongoing.Enable(True)
     360                  self._PRW_hospital_stay.SetText()
     361                  self._PRW_location.SetText()
     362                  self._PRW_episode.SetText()
     363                  self._PRW_procedure.SetText()
     364                  self._PRW_codes.SetText()
     365
     366                  self._PRW_procedure.SetFocus()
     367          #----------------------------------------------------------------
     368 -        def _refresh_from_existing(self):
     369                  self._DPRW_date.SetData(data = self.data['clin_when'])
     370                  if self.data['clin_end'] is None:
     371                          self._DPRW_end.SetText()
     372                          self._CHBOX_ongoing.Enable(True)
     373                          self._CHBOX_ongoing.SetValue(self.data['is_ongoing'])
     374                  else:
     375                          self._DPRW_end.SetData(data = self.data['clin_end'])
     376                          self._CHBOX_ongoing.Enable(False)
     377                          now = gmDateTime.pydt_now_here()
     378                          if self.data['clin_end'] > now:
     379                                  self._CHBOX_ongoing.SetValue(True)
     380                          else:
     381                                  self._CHBOX_ongoing.SetValue(False)
     382                  self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
     383                  self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure'])
     384
     385                  if self.data['pk_hospital_stay'] is None:
     386                          self._PRW_hospital_stay.SetText()
     387                          self._LBL_hospital_details.SetLabel(u'')
     388                          self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
     389                  else:
     390                          self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
     391                          self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = self.data['pk_hospital_stay']).format())
     392                          self._PRW_location.SetText()
     393
     394                  val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
     395                  self._PRW_codes.SetText(val, data)
     396
     397                  self._PRW_procedure.SetFocus()
     398          #----------------------------------------------------------------
     399 -        def _refresh_as_new_from_existing(self):
     400                  self._refresh_as_new()
     401                  self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
     402                  if self.data['pk_hospital_stay'] is None:
     403                          self._PRW_hospital_stay.SetText()
     404                          self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
     405                  else:
     406                          self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
     407                          self._PRW_location.SetText()
     408
     409                  self._PRW_procedure.SetFocus()
     410          #----------------------------------------------------------------
     411          # event handlers
     412          #----------------------------------------------------------------
     413 -        def _on_add_hospital_stay_button_pressed(self, evt):
     414                  # FIXME: this would benefit from setting the created stay
     415                  edit_hospital_stay(parent = self.GetParent())
     416                  evt.Skip()
     417          #----------------------------------------------------------------
     418 -        def _on_ongoing_checkbox_checked(self, event):
     419                  if self._CHBOX_ongoing.IsChecked():
     420                          end = self._DPRW_end.GetData()
     421                          if end is None:
     422                                  self._DPRW_end.display_as_valid(True)
     423                          else:
     424                                  end = end.get_pydt()
     425                                  now = gmDateTime.pydt_now_here()
     426                                  if end > now:
     427                                          self._DPRW_end.display_as_valid(True)
     428                                  else:
     429                                          self._DPRW_end.display_as_valid(False)
     430                  else:
     431                          self._DPRW_end.is_valid_timestamp()
     432                  event.Skip()
     433  #================================================================
     434  # hospital stay related widgets/functions
     435  #----------------------------------------------------------------
     436 -def manage_hospital_stays(parent=None):
     437
     438          pat = gmPerson.gmCurrentPatient()
     439          emr = pat.get_emr()
     440
     441          if parent is None:
     442                  parent = wx.GetApp().GetTopWindow()
     443          #-----------------------------------------
     444          def edit(stay=None):
     445                  return edit_hospital_stay(parent = parent, hospital_stay = stay)
     446          #-----------------------------------------
     447          def delete(stay=None):
     448                  if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
     449                          return True
     450                  gmDispatcher.send (
     451                          signal = u'statustext',
     452                          msg = _('Cannot delete hospital stay.'),
     453                          beep = True
     454                  )
     455                  return False
     456          #-----------------------------------------
     457          def refresh(lctrl):
     458                  stays = emr.get_hospital_stays()
     459                  items = [
     460                          [
     461                                  s['admission'].strftime('%Y-%m-%d'),
     462                                  gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
     463                                  s['episode'],
     464                                  gmTools.coalesce(s['hospital'], u'')
     465                          ] for s in stays
     466                  ]
     467                  lctrl.set_string_items(items = items)
     468                  lctrl.set_data(data = stays)
     469          #-----------------------------------------
     470          gmListWidgets.get_choices_from_list (
     471                  parent = parent,
     472                  msg = _('\nSelect the hospital stay you want to edit !\n'),
     473                  caption = _('Editing hospital stays ...'),
     474                  columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
     475                  single_selection = True,
     476                  edit_callback = edit,
     477                  new_callback = edit,
     478                  delete_callback = delete,
     479                  refresh_callback = refresh
     480          )
     481
     482  #----------------------------------------------------------------
     483 -def edit_hospital_stay(parent=None, hospital_stay=None):
     484          ea = cHospitalStayEditAreaPnl(parent = parent, id = -1)
     485          ea.data = hospital_stay
     486          ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit')
     487          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
     488          dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospital stay'), _('Editing a hospital stay')))
     489          if dlg.ShowModal() == wx.ID_OK:
     490                  dlg.Destroy()
     491                  return True
     492          dlg.Destroy()
     493          return False
     494  #----------------------------------------------------------------
     495 -class cHospitalStayPhraseWheel(gmPhraseWheel.cPhraseWheel):
     496          """Phrasewheel to allow selection of a hospital stay.
     497          """
     498 -        def __init__(self, *args, **kwargs):
     499
     500                  gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
     501
     502                  ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
     503
     504                  mp = gmMatchProvider.cMatchProvider_SQL2 (
     505                          queries = [
     506  u"""
     507  select
     508          pk_hospital_stay,
     509          descr
     510  from (
     511          select distinct on (pk_hospital_stay)
     512                  pk_hospital_stay,
     513                  descr
     514          from
     515                  (select
     516                          pk_hospital_stay,
     517                          (
     518                                  to_char(admission, 'YYYY-Mon-DD')
     519                                  || coalesce((' (' || hospital || '):'), ': ')
     520                                  || episode
     521                                  || coalesce((' (' || health_issue || ')'), '')
     522                          ) as descr
     523                   from
     524                          clin.v_pat_hospital_stays
     525                   where
     526                          %(ctxt_pat)s
     527
     528                          hospital %(fragment_condition)s
     529                                  or
     530                          episode %(fragment_condition)s
     531                                  or
     532                          health_issue %(fragment_condition)s
     533                  ) as the_stays
     534  ) as distinct_stays
     535  order by descr
     536  limit 25
     537  """                     ],
     538                          context = ctxt
     539                  )
     540                  mp.setThresholds(3, 4, 6)
     541                  mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
     542
     543                  self.matcher = mp
     544                  self.selection_only = True
     545  #----------------------------------------------------------------
     546  from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
     547
     548 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
     549
     550 -        def __init__(self, *args, **kwargs):
     551                  wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl.__init__(self, *args, **kwargs)
     552                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     553          #----------------------------------------------------------------
     554          # generic Edit Area mixin API
     555          #----------------------------------------------------------------
     556 -        def _valid_for_save(self):
     557
     558                  valid = True
     559
     560                  if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
     561                          valid = False
     562                          gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospital stay.'), beep = True)
     563
     564                  if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
     565                          if self._PRW_discharge.date is not None:
     566                                  if not self._PRW_discharge.date > self._PRW_admission.date:
     567                                          valid = False
     568                                          self._PRW_discharge.display_as_valid(False)
     569                                          gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), beep = True)
     570
     571                  if self._PRW_episode.GetValue().strip() == u'':
     572                          valid = False
     573                          self._PRW_episode.display_as_valid(False)
     574                          gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospital stay.'), beep = True)
     575
     576                  return (valid is True)
     577          #----------------------------------------------------------------
     578 -        def _save_as_new(self):
     579
     580                  pat = gmPerson.gmCurrentPatient()
     581                  emr = pat.get_emr()
     582                  stay = emr.add_hospital_stay(episode = self._PRW_episode.GetData(can_create = True))
     583                  stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
     584                  stay['admission'] = self._PRW_admission.GetData()
     585                  stay['discharge'] = self._PRW_discharge.GetData()
     586                  stay.save_payload()
     587
     588                  self.data = stay
     589                  return True
     590          #----------------------------------------------------------------
     591 -        def _save_as_update(self):
     592
     593                  self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
     594                  self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
     595                  self.data['admission'] = self._PRW_admission.GetData()
     596                  self.data['discharge'] = self._PRW_discharge.GetData()
     597                  self.data.save_payload()
     598
     599                  return True
     600          #----------------------------------------------------------------
     601 -        def _refresh_as_new(self):
     602                  self._PRW_hospital.SetText(value = u'')
     603                  self._PRW_episode.SetText(value = u'')
     604                  self._PRW_admission.SetText(data = gmDateTime.pydt_now_here())
     605                  self._PRW_discharge.SetText()
     606          #----------------------------------------------------------------
     607 -        def _refresh_from_existing(self):
     608                  if self.data['hospital'] is not None:
     609                          self._PRW_hospital.SetText(value = self.data['hospital'])
     610
     611                  if self.data['pk_episode'] is not None:
     612                          self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
     613
     614                  self._PRW_admission.SetText(data = self.data['admission'])
     615                  self._PRW_discharge.SetText(data = self.data['discharge'])
     616          #----------------------------------------------------------------
     617 -        def _refresh_as_new_from_existing(self):
     618                  print "this was not expected to be used in this edit area"
     619  #================================================================
     620  # encounter related widgets/functions
     621  #----------------------------------------------------------------
     622 -def start_new_encounter(emr=None):
     623          emr.start_new_encounter()
     624          gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True)
     625          time.sleep(0.5)
     626          gmGuiHelpers.gm_show_info (
     627                  _('\nA new encounter was started for the active patient.\n'),
     628                  _('Start of new encounter')
     629          )
     630  #----------------------------------------------------------------
     631  from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
     632
     633 -def edit_encounter(parent=None, encounter=None):
     634          if parent is None:
     635                  parent = wx.GetApp().GetTopWindow()
     636
     637          # FIXME: use generic dialog 2
     638          dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
     639          if dlg.ShowModal() == wx.ID_OK:
     640                  dlg.Destroy()
     641                  return True
     642          dlg.Destroy()
     643          return False
     644  #----------------------------------------------------------------
     645 -def manage_encounters(**kwargs):
     646          return select_encounters(**kwargs)
     647
     648 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
     649
     650          if patient is None:
     651                  patient = gmPerson.gmCurrentPatient()
     652
     653          if not patient.connected:
     654                  gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
     655                  return False
     656
     657          if parent is None:
     658                  parent = wx.GetApp().GetTopWindow()
     659
     660          emr = patient.get_emr()
     661
     662          #--------------------
     663          def refresh(lctrl):
     664                  if encounters is None:
     665                          encs = emr.get_encounters()
     666                  else:
     667                          encs = encounters
     668
     669                  items = [
     670                          [
     671                                  e['started'].strftime('%x  %H:%M'),
     672                                  e['last_affirmed'].strftime('%H:%M'),
     673                                  e['l10n_type'],
     674                                  gmTools.coalesce(e['reason_for_encounter'], u''),
     675                                  gmTools.coalesce(e['assessment_of_encounter'], u''),
     676                                  gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
     677                                  e['pk_encounter']
     678                          ] for e in encs
     679                  ]
     680                  lctrl.set_string_items(items = items)
     681                  lctrl.set_data(data = encs)
     682                  active_pk = emr.active_encounter['pk_encounter']
     683                  for idx in range(len(encs)):
     684                          e = encs[idx]
     685                          if e['pk_encounter'] == active_pk:
     686                                  lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
     687          #--------------------
     688          def new():
     689                  cfg_db = gmCfg.cCfgSQL()
     690                  # FIXME: look for MRU/MCU encounter type config here
     691                  enc_type = cfg_db.get2 (
     692                          option = u'encounter.default_type',
     693                          workplace = gmSurgery.gmCurrentPractice().active_workplace,
     694                          bias = u'user',
     695                          default = u'in surgery'
     696                  )
     697                  enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
     698                  return edit_encounter(parent = parent, encounter = enc)
     699          #--------------------
     700          def edit(enc=None):
     701                  return edit_encounter(parent = parent, encounter = enc)
     702          #--------------------
     703          def edit_active(enc=None):
     704                  return edit_encounter(parent = parent, encounter = emr.active_encounter)
     705          #--------------------
     706          def start_new(enc=None):
     707                  start_new_encounter(emr = emr)
     708                  return True
     709          #--------------------
     710          return gmListWidgets.get_choices_from_list (
     711                  parent = parent,
     712                  msg = _('\nBelow find the relevant encounters of the patient.\n'),
     713                  caption = _('Encounters ...'),
     714                  columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
     715                  can_return_empty = False,
     716                  single_selection = single_selection,
     717                  refresh_callback = refresh,
     718                  edit_callback = edit,
     719                  new_callback = new,
     720                  ignore_OK_button = ignore_OK_button,
     721                  left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
     722                  middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
     723          )
     724  #----------------------------------------------------------------
     725 -def ask_for_encounter_continuation(msg=None, caption=None, encounter=None, parent=None):
     726          """This is used as the callback when the EMR detects that the
     727             patient was here rather recently and wants to ask the
     728             provider whether to continue the recent encounter.
     729          """
     730          if parent is None:
     731                  parent = wx.GetApp().GetTopWindow()
     732
     733          dlg = gmGuiHelpers.c2ButtonQuestionDlg (
     734                  parent = None,
     735                  id = -1,
     736                  caption = caption,
     737                  question = msg,
     738                  button_defs = [
     739                          {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
     740                          {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
     741                  ],
     742                  show_checkbox = False
     743          )
     744
     745          result = dlg.ShowModal()
     746          dlg.Destroy()
     747
     748          if result == wx.ID_YES:
     749                  return True
     750
     751          return False
     752  #----------------------------------------------------------------
     753 -def manage_encounter_types(parent=None):
     754
     755          if parent is None:
     756                  parent = wx.GetApp().GetTopWindow()
     757
     758          #--------------------
     759          def edit(enc_type=None):
     760                  return edit_encounter_type(parent = parent, encounter_type = enc_type)
     761          #--------------------
     762          def delete(enc_type=None):
     763                  if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
     764                          return True
     765                  gmDispatcher.send (
     766                          signal = u'statustext',
     767                          msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
     768                          beep = True
     769                  )
     770                  return False
     771          #--------------------
     772          def refresh(lctrl):
     773                  enc_types = gmEMRStructItems.get_encounter_types()
     774                  lctrl.set_string_items(items = enc_types)
     775          #--------------------
     776          gmListWidgets.get_choices_from_list (
     777                  parent = parent,
     778                  msg = _('\nSelect the encounter type you want to edit !\n'),
     779                  caption = _('Managing encounter types ...'),
     780                  columns = [_('Local name'), _('Encounter type')],
     781                  single_selection = True,
     782                  edit_callback = edit,
     783                  new_callback = edit,
     784                  delete_callback = delete,
     785                  refresh_callback = refresh
     786          )
     787  #----------------------------------------------------------------
     788 -def edit_encounter_type(parent=None, encounter_type=None):
     789          ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1)
     790          ea.data = encounter_type
     791          ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit')
     792          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
     793          dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name')))
     794          if dlg.ShowModal() == wx.ID_OK:
     795                  return True
     796          return False
     797  #----------------------------------------------------------------
     798 -class cEncounterTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
     799          """Phrasewheel to allow selection of encounter type.
     800
     801          - user input interpreted as encounter type in English or local language
     802          - data returned is pk of corresponding encounter type or None
     803          """
     804 -        def __init__(self, *args, **kwargs):
     805
     806                  gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
     807
     808                  mp = gmMatchProvider.cMatchProvider_SQL2 (
     809                          queries = [
     810  u"""
     811  SELECT
     812          data,
     813          field_label,
     814          list_label
     815  FROM (
     816          SELECT DISTINCT ON (data) *
     817          FROM (
     818                  SELECT
     819                          pk AS data,
     820                          _(description) AS field_label,
     821                          case
     822                                  when _(description) = description then _(description)
     823                                  else _(description) || ' (' || description || ')'
     824                          end AS list_label
     825                  FROM
     826                          clin.encounter_type
     827                  WHERE
     828                          _(description) %(fragment_condition)s
     829                                  OR
     830                          description %(fragment_condition)s
     831          ) AS q_distinct_pk
     832  ) AS q_ordered
     833  ORDER BY
     834          list_label
     835  """                     ]
     836                  )
     837                  mp.setThresholds(2, 4, 6)
     838
     839                  self.matcher = mp
     840                  self.selection_only = True
     841                  self.picklist_delay = 50
     842  #----------------------------------------------------------------
     843 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
     844
     845 -        def __init__(self, *args, **kwargs):
     846
     847                  wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl.__init__(self, *args, **kwargs)
     848                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     849
     850  #               self.__register_interests()
     851          #-------------------------------------------------------
     852          # generic edit area API
     853          #-------------------------------------------------------
     854 -        def _valid_for_save(self):
     855                  if self.mode == 'edit':
     856                          if self._TCTRL_l10n_name.GetValue().strip() == u'':
     857                                  self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
     858                                  return False
     859                          self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
     860                          return True
     861
     862                  no_errors = True
     863
     864                  if self._TCTRL_l10n_name.GetValue().strip() == u'':
     865                          if self._TCTRL_name.GetValue().strip() == u'':
     866                                  self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
     867                                  no_errors = False
     868                          else:
     869                                  self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
     870                  else:
     871                          self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
     872
     873                  if self._TCTRL_name.GetValue().strip() == u'':
     874                          if self._TCTRL_l10n_name.GetValue().strip() == u'':
     875                                  self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False)
     876                                  no_errors = False
     877                          else:
     878                                  self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
     879                  else:
     880                          self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
     881
     882                  return no_errors
     883          #-------------------------------------------------------
     884 -        def _save_as_new(self):
     885                  enc_type = gmEMRStructItems.create_encounter_type (
     886                          description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''),
     887                          l10n_description = gmTools.coalesce (
     888                                  gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''),
     889                                  self._TCTRL_name.GetValue().strip()
     890                          )
     891                  )
     892                  if enc_type is None:
     893                          return False
     894                  self.data = enc_type
     895                  return True
     896          #-------------------------------------------------------
     897 -        def _save_as_update(self):
     898                  enc_type = gmEMRStructItems.update_encounter_type (
     899                          description = self._TCTRL_name.GetValue().strip(),
     900                          l10n_description = self._TCTRL_l10n_name.GetValue().strip()
     901                  )
     902                  if enc_type is None:
     903                          return False
     904                  self.data = enc_type
     905                  return True
     906          #-------------------------------------------------------
     907 -        def _refresh_as_new(self):
     908                  self._TCTRL_l10n_name.SetValue(u'')
     909                  self._TCTRL_name.SetValue(u'')
     910                  self._TCTRL_name.Enable(True)
     911          #-------------------------------------------------------
     912 -        def _refresh_from_existing(self):
     913                  self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
     914                  self._TCTRL_name.SetValue(self.data['description'])
     915                  # disallow changing type on all encounters by editing system name
     916                  self._TCTRL_name.Enable(False)
     917          #-------------------------------------------------------
     918 -        def _refresh_as_new_from_existing(self):
     919                  self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
     920                  self._TCTRL_name.SetValue(self.data['description'])
     921                  self._TCTRL_name.Enable(True)
     922          #-------------------------------------------------------
     923          # internal API
     924          #-------------------------------------------------------
     925  #       def __register_interests(self):
     926  #               return
     927  #----------------------------------------------------------------
     928  from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
     929
     930 -class cEncounterEditAreaPnl(wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl):
     931
     932 -        def __init__(self, *args, **kwargs):
     933                  try:
     934                          self.__encounter = kwargs['encounter']
     935                          del kwargs['encounter']
     936                  except KeyError:
     937                          self.__encounter = None
     938
     939                  try:
     940                          msg = kwargs['msg']
     941                          del kwargs['msg']
     942                  except KeyError:
     943                          msg = None
     944
     945                  wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
     946
     947                  self.refresh(msg = msg)
     948          #--------------------------------------------------------
     949          # external API
     950          #--------------------------------------------------------
     951 -        def refresh(self, encounter=None, msg=None):
     952
     953                  if msg is not None:
     954                          self._LBL_instructions.SetLabel(msg)
     955
     956                  if encounter is not None:
     957                          self.__encounter = encounter
     958
     959                  if self.__encounter is None:
     960                          return True
     961
     962                  # getting the patient via the encounter allows us to act
     963                  # on any encounter regardless of the currently active patient
     964                  pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
     965                  self._LBL_patient.SetLabel(pat.get_description_gender())
     966
     967                  self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
     968
     969                  fts = gmDateTime.cFuzzyTimestamp (
     970                          timestamp = self.__encounter['started'],
     971                          accuracy = gmDateTime.acc_minutes
     972                  )
     973                  self._PRW_start.SetText(fts.format_accurately(), data=fts)
     974
     975                  fts = gmDateTime.cFuzzyTimestamp (
     976                          timestamp = self.__encounter['last_affirmed'],
     977                          accuracy = gmDateTime.acc_minutes
     978                  )
     979                  self._PRW_end.SetText(fts.format_accurately(), data=fts)
     980
     981                  # RFE
     982                  self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
     983                  val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
     984                  self._PRW_rfe_codes.SetText(val, data)
     985
     986                  # AOE
     987                  self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
     988                  val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
     989                  self._PRW_aoe_codes.SetText(val, data)
     990
     991                  # last affirmed
     992                  if self.__encounter['last_affirmed'] == self.__encounter['started']:
     993                          self._PRW_end.SetFocus()
     994                  else:
     995                          self._TCTRL_aoe.SetFocus()
     996
     997                  return True
     998          #--------------------------------------------------------
     999 -        def __is_valid_for_save(self):
    1000
    1001                  if self._PRW_encounter_type.GetData() is None:
    1002                          self._PRW_encounter_type.SetBackgroundColour('pink')
    1003                          self._PRW_encounter_type.Refresh()
    1004                          self._PRW_encounter_type.SetFocus()
    1005                          return False
    1006                  self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    1007                  self._PRW_encounter_type.Refresh()
    1008
    1009                  # start
    1010                  if self._PRW_start.GetValue().strip() == u'':
    1011                          self._PRW_start.SetBackgroundColour('pink')
    1012                          self._PRW_start.Refresh()
    1013                          self._PRW_start.SetFocus()
    1014                          return False
    1015                  if not self._PRW_start.is_valid_timestamp():
    1016                          self._PRW_start.SetBackgroundColour('pink')
    1017                          self._PRW_start.Refresh()
    1018                          self._PRW_start.SetFocus()
    1019                          return False
    1020                  self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    1021                  self._PRW_start.Refresh()
    1022
    1023                  # last_affirmed
    1024                  if self._PRW_end.GetValue().strip() == u'':
    1025                          self._PRW_end.SetBackgroundColour('pink')
    1026                          self._PRW_end.Refresh()
    1027                          self._PRW_end.SetFocus()
    1028                          return False
    1029                  if not self._PRW_end.is_valid_timestamp():
    1030                          self._PRW_end.SetBackgroundColour('pink')
    1031                          self._PRW_end.Refresh()
    1032                          self._PRW_end.SetFocus()
    1033                          return False
    1034                  self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    1035                  self._PRW_end.Refresh()
    1036
    1037                  return True
    1038          #--------------------------------------------------------
    1039 -        def save(self):
    1040                  if not self.__is_valid_for_save():
    1041                          return False
    1042
    1043                  self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
    1044                  self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
    1045                  self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
    1046                  self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
    1047                  self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
    1048                  self.__encounter.save_payload()                 # FIXME: error checking
    1049
    1050                  self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
    1051                  self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
    1052
    1053                  return True
    1054  #----------------------------------------------------------------
    1055  # FIXME: use generic dialog 2
    1056 -class cEncounterEditAreaDlg(wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg):
    1057
    1058 -        def __init__(self, *args, **kwargs):
    1059                  encounter = kwargs['encounter']
    1060                  del kwargs['encounter']
    1061
    1062                  try:
    1063                          button_defs = kwargs['button_defs']
    1064                          del kwargs['button_defs']
    1065                  except KeyError:
    1066                          button_defs = None
    1067
    1068                  try:
    1069                          msg = kwargs['msg']
    1070                          del kwargs['msg']
    1071                  except KeyError:
    1072                          msg = None
    1073
    1074                  wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
    1075                  self.SetSize((450, 280))
    1076                  self.SetMinSize((450, 280))
    1077
    1078                  if button_defs is not None:
    1079                          self._BTN_save.SetLabel(button_defs[0][0])
    1080                          self._BTN_save.SetToolTipString(button_defs[0][1])
    1081                          self._BTN_close.SetLabel(button_defs[1][0])
    1082                          self._BTN_close.SetToolTipString(button_defs[1][1])
    1083                          self.Refresh()
    1084
    1085                  self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
    1086
    1087                  self.Fit()
    1088          #--------------------------------------------------------
    1089 -        def _on_save_button_pressed(self, evt):
    1090                  if self._PNL_edit_area.save():
    1091                          if self.IsModal():
    1092                                  self.EndModal(wx.ID_OK)
    1093                          else:
    1094                                  self.Close()
    1095  #----------------------------------------------------------------
    1096  from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
    1097
    1098 -class cActiveEncounterPnl(wxgActiveEncounterPnl.wxgActiveEncounterPnl):
    1099
    1100 -        def __init__(self, *args, **kwargs):
    1101                  wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs)
    1102                  self.__register_events()
    1103                  self.refresh()
    1104          #------------------------------------------------------------
    1105 -        def clear(self):
    1106                  self._TCTRL_encounter.SetValue(u'')
    1107                  self._TCTRL_encounter.SetToolTipString(u'')
    1108                  self._BTN_new.Enable(False)
    1109                  self._BTN_list.Enable(False)
    1110          #------------------------------------------------------------
    1111 -        def refresh(self):
    1112                  pat = gmPerson.gmCurrentPatient()
    1113                  if not pat.connected:
    1114                          self.clear()
    1115                          return
    1116
    1117                  enc = pat.get_emr().active_encounter
    1118                  self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
    1119                  self._TCTRL_encounter.SetToolTipString (
    1120                          _('The active encounter of the current patient:\n\n%s') %
    1121                                  enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
    1122                  )
    1123                  self._BTN_new.Enable(True)
    1124                  self._BTN_list.Enable(True)
    1125          #------------------------------------------------------------
    1126 -        def __register_events(self):
    1127                  self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
    1128
    1129                  gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
    1130                  # this would throw an exception due to concurrency issues:
    1131                  #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_refresh)
    1132                  gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
    1133                  gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
    1134                  gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
    1135          #------------------------------------------------------------
    1136          # event handler
    1137          #------------------------------------------------------------
    1138 -        def _schedule_clear(self):
    1139                  wx.CallAfter(self.clear)
    1140          #------------------------------------------------------------
    1141 -        def _schedule_refresh(self, *args, **kwargs):
    1142                  wx.CallAfter(self.refresh)
    1143                  return True
    1144          #------------------------------------------------------------
    1145 -        def _on_ldclick(self, event):
    1146                  pat = gmPerson.gmCurrentPatient()
    1147                  edit_encounter(encounter = pat.get_emr().active_encounter)
    1148          #------------------------------------------------------------
    1149 -        def _on_new_button_pressed(self, event):
    1150                  pat = gmPerson.gmCurrentPatient()
    1151                  start_new_encounter(emr = pat.get_emr())
    1152          #------------------------------------------------------------
    1153 -        def _on_list_button_pressed(self, event):
    1154                  select_encounters()
    1155  #================================================================
    1156  # episode related widgets/functions
    1157  #----------------------------------------------------------------
    1158 -def edit_episode(parent=None, episode=None):
    1159          ea = cEpisodeEditAreaPnl(parent = parent, id = -1)
    1160          ea.data = episode
    1161          ea.mode = gmTools.coalesce(episode, 'new', 'edit')
    1162          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
    1163          dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode')))
    1164          if dlg.ShowModal() == wx.ID_OK:
    1165                  return True
    1166          return False
    1167  #----------------------------------------------------------------
    1168 -def promote_episode_to_issue(parent=None, episode=None, emr=None):
    1169
    1170          created_new_issue = False
    1171
    1172          try:
    1173                  issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient'])
    1174          except gmExceptions.NoSuchBusinessObjectError:
    1175                  issue = None
    1176
    1177          if issue is None:
    1178                  issue = emr.add_health_issue(issue_name = episode['description'])
    1179                  created_new_issue = True
    1180          else:
    1181                  # issue exists already, so ask user
    1182                  dlg = gmGuiHelpers.c3ButtonQuestionDlg (
    1183                          parent,
    1184                          -1,
    1185                          caption = _('Promoting episode to health issue'),
    1186                          question = _(
    1187                                  'There already is a health issue\n'
    1188                                  '\n'
    1189                                  ' %s\n'
    1190                                  '\n'
    1191                                  'What do you want to do ?'
    1192                          ) % issue['description'],
    1193                          button_defs = [
    1194                                  {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False},
    1195                                  {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True}
    1196                          ]
    1197                  )
    1198                  use_existing = dlg.ShowModal()
    1199                  dlg.Destroy()
    1200
    1201                  if use_existing == wx.ID_CANCEL:
    1202                          return
    1203
    1204                  # user wants to create new issue with alternate name
    1205                  if use_existing == wx.ID_NO:
    1206                          # loop until name modified but non-empty or cancelled
    1207                          issue_name = episode['description']
    1208                          while issue_name == episode['description']:
    1209                                  dlg = wx.TextEntryDialog (
    1210                                          parent = parent,
    1211                                          message = _('Enter a short descriptive name for the new health issue:'),
    1212                                          caption = _('Creating a new health issue ...'),
    1213                                          defaultValue = issue_name,
    1214                                          style = wx.OK | wx.CANCEL | wx.CENTRE
    1215                                  )
    1216                                  decision = dlg.ShowModal()
    1217                                  if decision != wx.ID_OK:
    1218                                          dlg.Destroy()
    1219                                          return
    1220                                  issue_name = dlg.GetValue().strip()
    1221                                  dlg.Destroy()
    1222                                  if issue_name == u'':
    1223                                          issue_name = episode['description']
    1224
    1225                          issue = emr.add_health_issue(issue_name = issue_name)
    1226                          created_new_issue = True
    1227
    1228          # eventually move the episode to the issue
    1229          if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True):
    1230                  # user cancelled the move so delete just-created issue
    1231                  if created_new_issue:
    1232                          # shouldn't fail as it is completely new
    1233                          gmEMRStructItems.delete_health_issue(health_issue = issue)
    1234                  return
    1235
    1236          return
    1237  #----------------------------------------------------------------
    1238 -def move_episode_to_issue(episode=None, target_issue=None, save_to_backend=False):
    1239          """Prepare changing health issue for an episode.
    1240
    1241          Checks for two-open-episodes conflict. When this
    1242          function succeeds, the pk_health_issue has been set
    1243          on the episode instance and the episode should - for
    1244          all practical purposes - be ready for save_payload().
    1245          """
    1246          # episode is closed: should always work
    1247          if not episode['episode_open']:
    1248                  episode['pk_health_issue'] = target_issue['pk_health_issue']
    1249                  if save_to_backend:
    1250                          episode.save_payload()
    1251                  return True
    1252
    1253          # un-associate: should always work, too
    1254          if target_issue is None:
    1255                  episode['pk_health_issue'] = None
    1256                  if save_to_backend:
    1257                          episode.save_payload()
    1258                  return True
    1259
    1260          # try closing possibly expired episode on target issue if any
    1261          db_cfg = gmCfg.cCfgSQL()
    1262          epi_ttl = int(db_cfg.get2 (
    1263                  option = u'episode.ttl',
    1264                  workplace = gmSurgery.gmCurrentPractice().active_workplace,
    1265                  bias = 'user',
    1266                  default = 60                            # 2 months
    1267          ))
    1268          if target_issue.close_expired_episode(ttl=epi_ttl) is True:
    1269                  gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
    1270          existing_epi = target_issue.get_open_episode()
    1271
    1272          # no more open episode on target issue: should work now
    1273          if existing_epi is None:
    1274                  episode['pk_health_issue'] = target_issue['pk_health_issue']
    1275                  if save_to_backend:
    1276                          episode.save_payload()
    1277                  return True
    1278
    1279          # don't conflict on SELF ;-)
    1280          if existing_epi['pk_episode'] == episode['pk_episode']:
    1281                  episode['pk_health_issue'] = target_issue['pk_health_issue']
    1282                  if save_to_backend:
    1283                          episode.save_payload()
    1284                  return True
    1285
    1286          # we got two open episodes at once, ask user
    1287          move_range = episode.get_access_range()
    1288          exist_range = existing_epi.get_access_range()
    1289          question = _(
    1290                  'You want to associate the running episode:\n\n'
    1291                  ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
    1292                  'with the health issue:\n\n'
    1293                  ' "%(issue_name)s"\n\n'
    1294                  'There already is another episode running\n'
    1295                  'for this health issue:\n\n'
    1296                  ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
    1297                  'However, there can only be one running\n'
    1298                  'episode per health issue.\n\n'
    1299                  'Which episode do you want to close ?'
    1300          ) % {
    1301                  'new_epi_name': episode['description'],
    1302                  'new_epi_start': move_range[0].strftime('%m/%y'),
    1303                  'new_epi_end': move_range[1].strftime('%m/%y'),
    1304                  'issue_name': target_issue['description'],
    1305                  'old_epi_name': existing_epi['description'],
    1306                  'old_epi_start': exist_range[0].strftime('%m/%y'),
    1307                  'old_epi_end': exist_range[1].strftime('%m/%y')
    1308          }
    1309          dlg = gmGuiHelpers.c3ButtonQuestionDlg (
    1310                  parent = None,
    1311                  id = -1,
    1312                  caption = _('Resolving two-running-episodes conflict'),
    1313                  question = question,
    1314                  button_defs = [
    1315                          {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
    1316                          {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
    1317                  ]
    1318          )
    1319          decision = dlg.ShowModal()
    1320
    1321          if decision == wx.ID_CANCEL:
    1322                  # button 3: move cancelled by user
    1323                  return False
    1324
    1325          elif decision == wx.ID_YES:
    1326                  # button 1: close old episode
    1327                  existing_epi['episode_open'] = False
    1328                  existing_epi.save_payload()
    1329
    1330          elif decision == wx.ID_NO:
    1331                  # button 2: close new episode
    1332                  episode['episode_open'] = False
    1333
    1334          else:
    1335                  raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
    1336
    1337          episode['pk_health_issue'] = target_issue['pk_health_issue']
    1338          if save_to_backend:
    1339                  episode.save_payload()
    1340          return True
    1341  #----------------------------------------------------------------
    1342 -class cEpisodeListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
    1343
    1344          # FIXME: support pre-selection
    1345
    1346 -        def __init__(self, *args, **kwargs):
    1347
    1348                  episodes = kwargs['episodes']
    1349                  del kwargs['episodes']
    1350
    1351                  gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
    1352
    1353                  self.SetTitle(_('Select the episodes you are interested in ...'))
    1354                  self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')])
    1355                  self._LCTRL_items.set_string_items (
    1356                          items = [
    1357                                  [       epi['description'],
    1358                                          gmTools.bool2str(epi['episode_open'], _('ongoing'), u''),
    1359                                          gmTools.coalesce(epi['health_issue'], u'')
    1360                                  ]
    1361                                  for epi in episodes ]
    1362                  )
    1363                  self._LCTRL_items.set_column_widths()
    1364                  self._LCTRL_items.set_data(data = episodes)
    1365  #----------------------------------------------------------------
    1366 -class cEpisodeDescriptionPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1367          """Let user select an episode *description*.
    1368
    1369          The user can select an episode description from the previously
    1370          used descriptions across all episodes across all patients.
    1371
    1372          Selection is done with a phrasewheel so the user can
    1373          type the episode name and matches will be shown. Typing
    1374          "*" will show the entire list of episodes.
    1375
    1376          If the user types a description not existing yet a
    1377          new episode description will be returned.
    1378          """
    1379 -        def __init__(self, *args, **kwargs):
    1380
    1381                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    1382                          queries = [
    1383  u"""
    1384  SELECT DISTINCT ON (description)
    1385          description
    1386                  AS data,
    1387          description
    1388                  AS field_label,
    1389          description || ' ('
    1390          || CASE
    1391                  WHEN is_open IS TRUE THEN _('ongoing')
    1392                  ELSE _('closed')
    1393             END
    1394          || ')'
    1395                  AS list_label
    1396  FROM
    1397          clin.episode
    1398  WHERE
    1399          description %(fragment_condition)s
    1400  ORDER BY description
    1401  LIMIT 30
    1402  """
    1403                          ]
    1404                  )
    1405                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
    1406                  self.matcher = mp
    1407  #----------------------------------------------------------------
    1408 -class cEpisodeSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1409          """Let user select an episode.
    1410
    1411          The user can select an episode from the existing episodes of a
    1412          patient. Selection is done with a phrasewheel so the user
    1413          can type the episode name and matches will be shown. Typing
    1414          "*" will show the entire list of episodes. Closed episodes
    1415          will be marked as such. If the user types an episode name not
    1416          in the list of existing episodes a new episode can be created
    1417          from it if the programmer activated that feature.
    1418
    1419          If keyword <patient_id> is set to None or left out the control
    1420          will listen to patient change signals and therefore act on
    1421          gmPerson.gmCurrentPatient() changes.
    1422          """
    1423 -        def __init__(self, *args, **kwargs):
    1424
    1425                  ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
    1426
    1427                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    1428                          queries = [
    1429  u"""(
    1430
    1431  select
    1432          pk_episode
    1433                  as data,
    1434          description
    1435                  as field_label,
    1436          coalesce (
    1437                  description || ' - ' || health_issue,
    1438                  description
    1439          ) as list_label,
    1440          1 as rank
    1441  from
    1442          clin.v_pat_episodes
    1443  where
    1444          episode_open is true and
    1445          description %(fragment_condition)s
    1446          %(ctxt_pat)s
    1447
    1448  ) union all (
    1449
    1450  select
    1451          pk_episode
    1452                  as data,
    1453          description
    1454                  as field_label,
    1455          coalesce (
    1456                  description || _(' (closed)') || ' - ' || health_issue,
    1457                  description || _(' (closed)')
    1458          ) as list_label,
    1459          2 as rank
    1460  from
    1461          clin.v_pat_episodes
    1462  where
    1463          description %(fragment_condition)s and
    1464          episode_open is false
    1465          %(ctxt_pat)s
    1466
    1467  )
    1468
    1469  order by rank, list_label
    1470  limit 30"""
    1471  ],
    1472                          context = ctxt
    1473                  )
    1474
    1475                  try:
    1476                          kwargs['patient_id']
    1477                  except KeyError:
    1478                          kwargs['patient_id'] = None
    1479
    1480                  if kwargs['patient_id'] is None:
    1481                          self.use_current_patient = True
    1482                          self.__register_patient_change_signals()
    1483                          pat = gmPerson.gmCurrentPatient()
    1484                          if pat.connected:
    1485                                  mp.set_context('pat', pat.ID)
    1486                  else:
    1487                          self.use_current_patient = False
    1488                          self.__patient_id = int(kwargs['patient_id'])
    1489                          mp.set_context('pat', self.__patient_id)
    1490
    1491                  del kwargs['patient_id']
    1492
    1493                  gmPhraseWheel.cPhraseWheel.__init__ (
    1494                          self,
    1495                          *args,
    1496                          **kwargs
    1497                  )
    1498                  self.matcher = mp
    1499          #--------------------------------------------------------
    1500          # external API
    1501          #--------------------------------------------------------
    1502 -        def set_patient(self, patient_id=None):
    1503                  if self.use_current_patient:
    1504                          return False
    1505                  self.__patient_id = int(patient_id)
    1506                  self.set_context('pat', self.__patient_id)
    1507                  return True
    1508          #--------------------------------------------------------
    1509 -        def GetData(self, can_create=False, as_instance=False, is_open=False):
    1510                  self.__is_open_for_create_data = is_open                # used (only) in _create_data()
    1511                  return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
    1512          #--------------------------------------------------------
    1513 -        def _create_data(self):
    1514
    1515                  epi_name = self.GetValue().strip()
    1516                  if epi_name == u'':
    1517                          gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
    1518                          _log.debug('cannot create episode without name')
    1519                          return
    1520
    1521                  if self.use_current_patient:
    1522                          pat = gmPerson.gmCurrentPatient()
    1523                  else:
    1524                          pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
    1525
    1526                  emr = pat.get_emr()
    1527                  epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
    1528                  if epi is None:
    1529                          self.data = {}
    1530                  else:
    1531                          self.SetText (
    1532                                  value = epi_name,
    1533                                  data = epi['pk_episode']
    1534                          )
    1535          #--------------------------------------------------------
    1536 -        def _data2instance(self):
    1537                  return gmEMRStructItems.cEpisode(aPK_obj = self.GetData())
    1538          #--------------------------------------------------------
    1539          # internal API
    1540          #--------------------------------------------------------
    1541 -        def __register_patient_change_signals(self):
    1542                  gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
    1543                  gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
    1544          #--------------------------------------------------------
    1545 -        def _pre_patient_selection(self):
    1546                  return True
    1547          #--------------------------------------------------------
    1548 -        def _post_patient_selection(self):
    1549                  if self.use_current_patient:
    1550                          patient = gmPerson.gmCurrentPatient()
    1551                          self.set_context('pat', patient.ID)
    1552                  return True
    1553  #----------------------------------------------------------------
    1554  from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
    1555
    1556 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
    1557
    1558 -        def __init__(self, *args, **kwargs):
    1559
    1560                  try:
    1561                          episode = kwargs['episode']
    1562                          del kwargs['episode']
    1563                  except KeyError:
    1564                          episode = None
    1565
    1566                  wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
    1567                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1568
    1569                  self.data = episode
    1570          #----------------------------------------------------------------
    1571          # generic Edit Area mixin API
    1572          #----------------------------------------------------------------
    1573 -        def _valid_for_save(self):
    1574
    1575                  errors = False
    1576
    1577                  if len(self._PRW_description.GetValue().strip()) == 0:
    1578                          errors = True
    1579                          self._PRW_description.display_as_valid(False)
    1580                          self._PRW_description.SetFocus()
    1581                  else:
    1582                          self._PRW_description.display_as_valid(True)
    1583                  self._PRW_description.Refresh()
    1584
    1585                  return not errors
    1586          #----------------------------------------------------------------
    1587 -        def _save_as_new(self):
    1588
    1589                  pat = gmPerson.gmCurrentPatient()
    1590                  emr = pat.get_emr()
    1591
    1592                  epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
    1593                  epi['summary'] = self._TCTRL_status.GetValue().strip()
    1594                  epi['episode_open'] = not self._CHBOX_closed.IsChecked()
    1595                  epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
    1596
    1597                  issue_name = self._PRW_issue.GetValue().strip()
    1598                  if len(issue_name) != 0:
    1599                          epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
    1600                          issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
    1601
    1602                          if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
    1603                                  gmDispatcher.send (
    1604                                          signal = 'statustext',
    1605                                          msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
    1606                                                  epi['description'],
    1607                                                  issue['description']
    1608                                          )
    1609                                  )
    1610                                  gmEMRStructItems.delete_episode(episode = epi)
    1611                                  return False
    1612
    1613                  epi.save()
    1614
    1615                  epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
    1616
    1617                  self.data = epi
    1618                  return True
    1619          #----------------------------------------------------------------
    1620 -        def _save_as_update(self):
    1621
    1622                  self.data['description'] = self._PRW_description.GetValue().strip()
    1623                  self.data['summary'] = self._TCTRL_status.GetValue().strip()
    1624                  self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
    1625                  self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
    1626
    1627                  issue_name = self._PRW_issue.GetValue().strip()
    1628                  if len(issue_name) == 0:
    1629                          self.data['pk_health_issue'] = None
    1630                  else:
    1631                          self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
    1632                          issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
    1633
    1634                          if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
    1635                                  gmDispatcher.send (
    1636                                          signal = 'statustext',
    1637                                          msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
    1638                                                  self.data['description'],
    1639                                                  issue['description']
    1640                                          )
    1641                                  )
    1642                                  return False
    1643
    1644                  self.data.save()
    1645                  self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
    1646
    1647                  return True
    1648          #----------------------------------------------------------------
    1649 -        def _refresh_as_new(self):
    1650                  if self.data is None:
    1651                          ident = gmPerson.gmCurrentPatient()
    1652                  else:
    1653                          ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
    1654                  self._TCTRL_patient.SetValue(ident.get_description_gender())
    1655                  self._PRW_issue.SetText()
    1656                  self._PRW_description.SetText()
    1657                  self._TCTRL_status.SetValue(u'')
    1658                  self._PRW_certainty.SetText()
    1659                  self._CHBOX_closed.SetValue(False)
    1660                  self._PRW_codes.SetText()
    1661          #----------------------------------------------------------------
    1662 -        def _refresh_from_existing(self):
    1663                  ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
    1664                  self._TCTRL_patient.SetValue(ident.get_description_gender())
    1665
    1666                  if self.data['pk_health_issue'] is not None:
    1667                          self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue'])
    1668
    1669                  self._PRW_description.SetText(self.data['description'], data=self.data['description'])
    1670
    1671                  self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
    1672
    1673                  if self.data['diagnostic_certainty_classification'] is not None:
    1674                          self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
    1675
    1676                  self._CHBOX_closed.SetValue(not self.data['episode_open'])
    1677
    1678                  val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
    1679                  self._PRW_codes.SetText(val, data)
    1680          #----------------------------------------------------------------
    1681 -        def _refresh_as_new_from_existing(self):
    1682                  self._refresh_as_new()
    1683  #================================================================
    1684  # health issue related widgets/functions
    1685  #----------------------------------------------------------------
    1686 -def edit_health_issue(parent=None, issue=None):
    1687          ea = cHealthIssueEditAreaPnl(parent = parent, id = -1)
    1688          ea.data = issue
    1689          ea.mode = gmTools.coalesce(issue, 'new', 'edit')
    1690          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None))
    1691          dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue')))
    1692          if dlg.ShowModal() == wx.ID_OK:
    1693                  return True
    1694          return False
    1695  #----------------------------------------------------------------
    1696 -class cIssueListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
    1697
    1698          # FIXME: support pre-selection
    1699
    1700 -        def __init__(self, *args, **kwargs):
    1701
    1702                  issues = kwargs['issues']
    1703                  del kwargs['issues']
    1704
    1705                  gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
    1706
    1707                  self.SetTitle(_('Select the health issues you are interested in ...'))
    1708                  self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
    1709
    1710                  for issue in issues:
    1711                          if issue['is_confidential']:
    1712                                  row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
    1713                                  self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
    1714                          else:
    1715                                  row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
    1716
    1717                          self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
    1718                          if issue['clinically_relevant']:
    1719                                  self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
    1720                          if issue['is_active']:
    1721                                  self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
    1722                          if issue['is_cause_of_death']:
    1723                                  self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
    1724
    1725                  self._LCTRL_items.set_column_widths()
    1726                  self._LCTRL_items.set_data(data = issues)
    1727  #----------------------------------------------------------------
    1728 -class cIssueSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1729          """Let the user select a health issue.
    1730
    1731          The user can select a health issue from the existing issues
    1732          of a patient. Selection is done with a phrasewheel so the user
    1733          can type the issue name and matches will be shown. Typing
    1734          "*" will show the entire list of issues. Inactive issues
    1735          will be marked as such. If the user types an issue name not
    1736          in the list of existing issues a new issue can be created
    1737          from it if the programmer activated that feature.
    1738
    1739          If keyword <patient_id> is set to None or left out the control
    1740          will listen to patient change signals and therefore act on
    1741          gmPerson.gmCurrentPatient() changes.
    1742          """
    1743 -        def __init__(self, *args, **kwargs):
    1744
    1745                  ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
    1746
    1747                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    1748                          # FIXME: consider clin.health_issue.clinically_relevant
    1749                          queries = [
    1750  u"""
    1751  SELECT
    1752          data,
    1753          field_label,
    1754          list_label
    1755  FROM ((
    1756          SELECT
    1757                  pk_health_issue AS data,
    1758                  description AS field_label,
    1759                  description AS list_label
    1760          FROM clin.v_health_issues
    1761          WHERE
    1762                  is_active IS true
    1763                          AND
    1764                  description %(fragment_condition)s
    1765                          AND
    1766                  %(ctxt_pat)s
    1767
    1768          ) UNION (
    1769
    1770          SELECT
    1771                  pk_health_issue AS data,
    1772                  description AS field_label,
    1773                  description || _(' (inactive)') AS list_label
    1774          FROM clin.v_health_issues
    1775          WHERE
    1776                  is_active IS false
    1777                          AND
    1778                  description %(fragment_condition)s
    1779                          AND
    1780                  %(ctxt_pat)s
    1781  )) AS union_query
    1782  ORDER BY
    1783          list_label"""],
    1784                          context = ctxt
    1785                  )
    1786
    1787                  try: kwargs['patient_id']
    1788                  except KeyError: kwargs['patient_id'] = None
    1789
    1790                  if kwargs['patient_id'] is None:
    1791                          self.use_current_patient = True
    1792                          self.__register_patient_change_signals()
    1793                          pat = gmPerson.gmCurrentPatient()
    1794                          if pat.connected:
    1795                                  mp.set_context('pat', pat.ID)
    1796                  else:
    1797                          self.use_current_patient = False
    1798                          self.__patient_id = int(kwargs['patient_id'])
    1799                          mp.set_context('pat', self.__patient_id)
    1800
    1801                  del kwargs['patient_id']
    1802
    1803                  gmPhraseWheel.cPhraseWheel.__init__ (
    1804                          self,
    1805                          *args,
    1806                          **kwargs
    1807                  )
    1808                  self.matcher = mp
    1809          #--------------------------------------------------------
    1810          # external API
    1811          #--------------------------------------------------------
    1812 -        def set_patient(self, patient_id=None):
    1813                  if self.use_current_patient:
    1814                          return False
    1815                  self.__patient_id = int(patient_id)
    1816                  self.set_context('pat', self.__patient_id)
    1817                  return True
    1818          #--------------------------------------------------------
    1819 -        def GetData(self, can_create=False, is_open=False):
    1820                  if self.data is None:
    1821                          if can_create:
    1822                                  issue_name = self.GetValue().strip()
    1823
    1824                                  if self.use_current_patient:
    1825                                          pat = gmPerson.gmCurrentPatient()
    1826                                  else:
    1827                                          pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
    1828                                  emr = pat.get_emr()
    1829
    1830                                  issue = emr.add_health_issue(issue_name = issue_name)
    1831                                  if issue is None:
    1832                                          self.data = None
    1833                                  else:
    1834                                          self.data = issue['pk_health_issue']
    1835
    1836                  return gmPhraseWheel.cPhraseWheel.GetData(self)
    1837          #--------------------------------------------------------
    1838          # internal API
    1839          #--------------------------------------------------------
    1840 -        def __register_patient_change_signals(self):
    1841                  gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
    1842                  gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
    1843          #--------------------------------------------------------
    1844 -        def _pre_patient_selection(self):
    1845                  return True
    1846          #--------------------------------------------------------
    1847 -        def _post_patient_selection(self):
    1848                  if self.use_current_patient:
    1849                          patient = gmPerson.gmCurrentPatient()
    1850                          self.set_context('pat', patient.ID)
    1851                  return True
    1852  #------------------------------------------------------------
    1853 -class cIssueSelectionDlg(wxgIssueSelectionDlg.wxgIssueSelectionDlg):
    1854
    1855 -        def __init__(self, *args, **kwargs):
    1856                  try:
    1857                          msg = kwargs['message']
    1858                  except KeyError:
    1859                          msg = None
    1860                  del kwargs['message']
    1861                  wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
    1862                  if msg is not None:
    1863                          self._lbl_message.SetLabel(label=msg)
    1864          #--------------------------------------------------------
    1865 -        def _on_OK_button_pressed(self, event):
    1866                  event.Skip()
    1867                  pk_issue = self._PhWheel_issue.GetData(can_create=True)
    1868                  if pk_issue is None:
    1869                          gmGuiHelpers.gm_show_error (
    1870                                  _('Cannot create new health issue:\n [%(issue)s]') % {'issue': self._PhWheel_issue.GetValue().strip()},
    1871                                  _('Selecting health issue')
    1872                          )
    1873                          return False
    1874                  return True
    1875  #------------------------------------------------------------
    1876  from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
    1877
    1878 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
    1879          """Panel encapsulating health issue edit area functionality."""
    1880
    1881 -        def __init__(self, *args, **kwargs):
    1882
    1883                  try:
    1884                          issue = kwargs['issue']
    1885                  except KeyError:
    1886                           issue = None
    1887
    1888                  wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
    1889
    1890                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1891
    1892                  # FIXME: include more sources: coding systems/other database columns
    1893                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    1894                          queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
    1895                  )
    1896                  mp.setThresholds(1, 3, 5)
    1897                  self._PRW_condition.matcher = mp
    1898
    1899                  mp = gmMatchProvider.cMatchProvider_SQL2 (
    1900                          queries = [u"""
    1901  select distinct on (grouping) grouping, grouping from (
    1902
    1903          select rank, grouping from ((
    1904
    1905                  select
    1906                          grouping,
    1907                          1 as rank
    1908                  from
    1909                          clin.health_issue
    1910                  where
    1911                          grouping %%(fragment_condition)s
    1912                                  and
    1913                          (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
    1914
    1915          ) union (
    1916
    1917                  select
    1918                          grouping,
    1919                          2 as rank
    1920                  from
    1921                          clin.health_issue
    1922                  where
    1923                          grouping %%(fragment_condition)s
    1924
    1925          )) as union_result
    1926
    1927          order by rank
    1928
    1929  ) as order_result
    1930
    1931  limit 50""" % gmPerson.gmCurrentPatient().ID
    1932                          ]
    1933                  )
    1934                  mp.setThresholds(1, 3, 5)
    1935                  self._PRW_grouping.matcher = mp
    1936
    1937                  self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
    1938                  self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
    1939
    1940                  self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
    1941                  self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
    1942
    1943                  self._PRW_year_noted.Enable(True)
    1944
    1945                  self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
    1946
    1947                  self.data = issue
    1948          #----------------------------------------------------------------
    1949          # generic Edit Area mixin API
    1950          #----------------------------------------------------------------
    1951 -        def _valid_for_save(self):
    1952
    1953                  if self._PRW_condition.GetValue().strip() == '':
    1954                          self._PRW_condition.display_as_valid(False)
    1955                          self._PRW_condition.SetFocus()
    1956                          return False
    1957                  self._PRW_condition.display_as_valid(True)
    1958                  self._PRW_condition.Refresh()
    1959
    1960                  # FIXME: sanity check age/year diagnosed
    1961                  age_noted = self._PRW_age_noted.GetValue().strip()
    1962                  if age_noted != '':
    1963                          if gmDateTime.str2interval(str_interval = age_noted) is None:
    1964                                  self._PRW_age_noted.display_as_valid(False)
    1965                                  self._PRW_age_noted.SetFocus()
    1966                                  return False
    1967                  self._PRW_age_noted.display_as_valid(True)
    1968
    1969                  return True
    1970          #----------------------------------------------------------------
    1971 -        def _save_as_new(self):
    1972                  pat = gmPerson.gmCurrentPatient()
    1973                  emr = pat.get_emr()
    1974
    1975                  issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
    1976
    1977                  side = u''
    1978                  if self._ChBOX_left.GetValue():
    1979                          side += u's'
    1980                  if self._ChBOX_right.GetValue():
    1981                          side += u'd'
    1982                  issue['laterality'] = side
    1983
    1984                  issue['summary'] = self._TCTRL_status.GetValue().strip()
    1985                  issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
    1986                  issue['grouping'] = self._PRW_grouping.GetValue().strip()
    1987                  issue['is_active'] = self._ChBOX_active.GetValue()
    1988                  issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
    1989                  issue['is_confidential'] = self._ChBOX_confidential.GetValue()
    1990                  issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
    1991
    1992                  age_noted = self._PRW_age_noted.GetData()
    1993                  if age_noted is not None:
    1994                          issue['age_noted'] = age_noted
    1995
    1996                  issue.save()
    1997
    1998                  issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
    1999
    2000                  self.data = issue
    2001                  return True
    2002          #----------------------------------------------------------------
    2003 -        def _save_as_update(self):
    2004
    2005                  self.data['description'] = self._PRW_condition.GetValue().strip()
    2006
    2007                  side = u''
    2008                  if self._ChBOX_left.GetValue():
    2009                          side += u's'
    2010                  if self._ChBOX_right.GetValue():
    2011                          side += u'd'
    2012                  self.data['laterality'] = side
    2013
    2014                  self.data['summary'] = self._TCTRL_status.GetValue().strip()
    2015                  self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
    2016                  self.data['grouping'] = self._PRW_grouping.GetValue().strip()
    2017                  self.data['is_active'] = bool(self._ChBOX_active.GetValue())
    2018                  self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
    2019                  self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
    2020                  self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
    2021
    2022                  age_noted = self._PRW_age_noted.GetData()
    2023                  if age_noted is not None:
    2024                          self.data['age_noted'] = age_noted
    2025
    2026                  self.data.save()
    2027                  self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
    2028
    2029                  return True
    2030          #----------------------------------------------------------------
    2031 -        def _refresh_as_new(self):
    2032                  self._PRW_condition.SetText()
    2033                  self._ChBOX_left.SetValue(0)
    2034                  self._ChBOX_right.SetValue(0)
    2035                  self._PRW_codes.SetText()
    2036                  self._on_leave_codes()
    2037                  self._PRW_certainty.SetText()
    2038                  self._PRW_grouping.SetText()
    2039                  self._TCTRL_status.SetValue(u'')
    2040                  self._PRW_age_noted.SetText()
    2041                  self._PRW_year_noted.SetText()
    2042                  self._ChBOX_active.SetValue(0)
    2043                  self._ChBOX_relevant.SetValue(1)
    2044                  self._ChBOX_confidential.SetValue(0)
    2045                  self._ChBOX_caused_death.SetValue(0)
    2046
    2047                  return True
    2048          #----------------------------------------------------------------
    2049 -        def _refresh_from_existing(self):
    2050                  self._PRW_condition.SetText(self.data['description'])
    2051
    2052                  lat = gmTools.coalesce(self.data['laterality'], '')
    2053                  if lat.find('s') == -1:
    2054                          self._ChBOX_left.SetValue(0)
    2055                  else:
    2056                          self._ChBOX_left.SetValue(1)
    2057                  if lat.find('d') == -1:
    2058                          self._ChBOX_right.SetValue(0)
    2059                  else:
    2060                          self._ChBOX_right.SetValue(1)
    2061
    2062                  val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
    2063                  self._PRW_codes.SetText(val, data)
    2064                  self._on_leave_codes()
    2065
    2066                  if self.data['diagnostic_certainty_classification'] is not None:
    2067                          self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
    2068                  self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
    2069                  self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
    2070
    2071                  if self.data['age_noted'] is None:
    2072                          self._PRW_age_noted.SetText()
    2073                  else:
    2074                          self._PRW_age_noted.SetText (
    2075                                  value = '%sd' % self.data['age_noted'].days,
    2076                                  data = self.data['age_noted']
    2077                          )
    2078
    2079                  self._ChBOX_active.SetValue(self.data['is_active'])
    2080                  self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
    2081                  self._ChBOX_confidential.SetValue(self.data['is_confidential'])
    2082                  self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
    2083
    2084                  # this dance should assure self._PRW_year_noted gets set -- but it doesn't ...
    2085  #               self._PRW_age_noted.SetFocus()
    2086  #               self._PRW_condition.SetFocus()
    2087
    2088                  return True
    2089          #----------------------------------------------------------------
    2090 -        def _refresh_as_new_from_existing(self):
    2091                  return self._refresh_as_new()
    2092          #--------------------------------------------------------
    2093          # internal helpers
    2094          #--------------------------------------------------------
    2095 -        def _on_leave_codes(self, *args, **kwargs):
    2096                  if not self._PRW_codes.IsModified():
    2097                          return True
    2098
    2099                  self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
    2100          #--------------------------------------------------------
    2101 -        def _on_leave_age_noted(self, *args, **kwargs):
    2102
    2103                  if not self._PRW_age_noted.IsModified():
    2104                          return True
    2105
    2106                  str_age = self._PRW_age_noted.GetValue().strip()
    2107
    2108                  if str_age == u'':
    2109                          wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
    2110                          return True
    2111
    2112                  age = gmDateTime.str2interval(str_interval = str_age)
    2113
    2114                  if age is None:
    2115                          gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
    2116                          self._PRW_age_noted.SetBackgroundColour('pink')
    2117                          self._PRW_age_noted.Refresh()
    2118                          wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
    2119                          return True
    2120
    2121                  pat = gmPerson.gmCurrentPatient()
    2122                  if pat['dob'] is not None:
    2123                          max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
    2124
    2125                          if age >= max_age:
    2126                                  gmDispatcher.send (
    2127                                          signal = 'statustext',
    2128                                          msg = _(
    2129                                                  'Health issue cannot have been noted at age %s. Patient is only %s old.'
    2130                                          ) % (age, pat.get_medical_age())
    2131                                  )
    2132                                  self._PRW_age_noted.SetBackgroundColour('pink')
    2133                                  self._PRW_age_noted.Refresh()
    2134                                  wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
    2135                                  return True
    2136
    2137                  self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    2138                  self._PRW_age_noted.Refresh()
    2139                  self._PRW_age_noted.SetData(data=age)
    2140
    2141                  if pat['dob'] is not None:
    2142                          fts = gmDateTime.cFuzzyTimestamp (
    2143                                  timestamp = pat['dob'] + age,
    2144                                  accuracy = gmDateTime.acc_months
    2145                          )
    2146                          wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
    2147                          # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB
    2148                          #wx.CallAfter(self._ChBOX_active.SetFocus)
    2149                          # if we do the following instead it will take us to the save/update button ...
    2150                          #wx.CallAfter(self.Navigate)
    2151
    2152                  return True
    2153          #--------------------------------------------------------
    2154 -        def _on_leave_year_noted(self, *args, **kwargs):
    2155
    2156                  if not self._PRW_year_noted.IsModified():
    2157                          return True
    2158
    2159                  year_noted = self._PRW_year_noted.GetData()
    2160
    2161                  if year_noted is None:
    2162                          if self._PRW_year_noted.GetValue().strip() == u'':
    2163                                  wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
    2164                                  return True
    2165                          self._PRW_year_noted.SetBackgroundColour('pink')
    2166                          self._PRW_year_noted.Refresh()
    2167                          wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
    2168                          return True
    2169
    2170                  year_noted = year_noted.get_pydt()
    2171
    2172                  if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
    2173                          gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
    2174                          self._PRW_year_noted.SetBackgroundColour('pink')
    2175                          self._PRW_year_noted.Refresh()
    2176                          wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
    2177                          return True
    2178
    2179                  self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
    2180                  self._PRW_year_noted.Refresh()
    2181
    2182                  pat = gmPerson.gmCurrentPatient()
    2183                  if pat['dob'] is not None:
    2184                          issue_age = year_noted - pat['dob']
    2185                          str_age = gmDateTime.format_interval_medically(interval = issue_age)
    2186                          wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
    2187
    2188                  return True
    2189          #--------------------------------------------------------
    2190 -        def _on_modified_age_noted(self, *args, **kwargs):
    2191                  wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
    2192                  return True
    2193          #--------------------------------------------------------
    2194 -        def _on_modified_year_noted(self, *args, **kwargs):
    2195                  wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
    2196                  return True
    2197  #================================================================
    2198  # diagnostic certainty related widgets/functions
    2199  #----------------------------------------------------------------
    2200 -class cDiagnosticCertaintyClassificationPhraseWheel(gmPhraseWheel.cPhraseWheel):
    2201
    2202 -        def __init__(self, *args, **kwargs):
    2203
    2204                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
    2205
    2206                  self.selection_only = False                     # can be NULL, too
    2207
    2208                  mp = gmMatchProvider.cMatchProvider_FixedList (
    2209                          aSeq = [
    2210                                  {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
    2211                                  {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
    2212                                  {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
    2213                                  {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
    2214                          ]
    2215                  )
    2216                  mp.setThresholds(1, 2, 4)
    2217                  self.matcher = mp
    2218
    2219                  self.SetToolTipString(_(
    2220                          "The diagnostic classification or grading of this assessment.\n"
    2221                          "\n"
    2222                          "This documents how certain one is about this being a true diagnosis."
    2223                  ))
    2224  #================================================================
    2225  # MAIN
    2226  #----------------------------------------------------------------
    2227  if __name__ == '__main__':
    2228
    2229          #================================================================
    2230 -        class testapp (wx.App):
    2231                          """
    2232                          Test application for testing EMR struct widgets
    2233                          """
    2234                          #--------------------------------------------------------
    2235 -                        def OnInit (self):
    2236                                  """
    2237                                  Create test application UI
    2238                                  """
    2239                                  frame = wx.Frame (
    2240                                                          None,
    2241                                                          -4,
    2242                                                          'Testing EMR struct widgets',
    2243                                                          size=wx.Size(600, 400),
    2244                                                          style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
    2245                                                  )
    2246                                  filemenu= wx.Menu()
    2247                                  filemenu.AppendSeparator()
    2248                                  filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
    2249
    2250                                  # Creating the menubar.
    2251                                  menuBar = wx.MenuBar()
    2252                                  menuBar.Append(filemenu,"&File")
    2253
    2254                                  frame.SetMenuBar(menuBar)
    2255
    2256                                  txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
    2257                                  wx.DefaultPosition, wx.DefaultSize, 0 )
    2258
    2259                                  # event handlers
    2260                                  wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
    2261
    2262                                  # patient EMR
    2263                                  self.__pat = gmPerson.gmCurrentPatient()
    2264
    2265                                  frame.Show(1)
    2266                                  return 1
    2267                          #--------------------------------------------------------
    2268 -                        def OnCloseWindow (self, e):
    2269                                  """
    2270                                  Close test aplication
    2271                                  """
    2272                                  self.ExitMainLoop ()
    2273          #----------------------------------------------------------------
    2274 -        def test_encounter_edit_area_panel():
    2275                  app = wx.PyWidgetTester(size = (200, 300))
    2276                  emr = pat.get_emr()
    2277                  enc = emr.active_encounter
    2278                  #enc = gmEMRStructItems.cEncounter(1)
    2279                  pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
    2280                  app.frame.Show(True)
    2281                  app.MainLoop()
    2282                  return
    2283          #----------------------------------------------------------------
    2284 -        def test_encounter_edit_area_dialog():
    2285                  app = wx.PyWidgetTester(size = (200, 300))
    2286                  emr = pat.get_emr()
    2287                  enc = emr.active_encounter
    2288                  #enc = gmEMRStructItems.cEncounter(1)
    2289
    2290                  dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
    2291                  dlg.ShowModal()
    2292
    2293  #               pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc)
    2294  #               app.frame.Show(True)
    2295  #               app.MainLoop()
    2296          #----------------------------------------------------------------
    2297 -        def test_epsiode_edit_area_pnl():
    2298                  app = wx.PyWidgetTester(size = (200, 300))
    2299                  emr = pat.get_emr()
    2300                  epi = emr.get_episodes()[0]
    2301                  pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
    2302                  app.frame.Show(True)
    2303                  app.MainLoop()
    2304          #----------------------------------------------------------------
    2305 -        def test_episode_edit_area_dialog():
    2306                  app = wx.PyWidgetTester(size = (200, 300))
    2307                  emr = pat.get_emr()
    2308                  epi = emr.get_episodes()[0]
    2309                  edit_episode(parent=app.frame, episode=epi)
    2310          #----------------------------------------------------------------
    2311 -        def test_hospital_stay_prw():
    2312                  app = wx.PyWidgetTester(size = (400, 40))
    2313                  app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
    2314                  app.MainLoop()
    2315          #----------------------------------------------------------------
    2316 -        def test_episode_selection_prw():
    2317                  app = wx.PyWidgetTester(size = (400, 40))
    2318                  app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
    2319  #               app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID)
    2320                  app.MainLoop()
    2321          #----------------------------------------------------------------
    2322 -        def test_health_issue_edit_area_dlg():
    2323                  app = wx.PyWidgetTester(size = (200, 300))
    2324                  edit_health_issue(parent=app.frame, issue=None)
    2325          #----------------------------------------------------------------
    2326 -        def test_health_issue_edit_area_pnl():
    2327                  app = wx.PyWidgetTester(size = (200, 300))
    2328                  app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
    2329                  app.MainLoop()
    2330          #----------------------------------------------------------------
    2331 -        def test_edit_procedure():
    2332                  app = wx.PyWidgetTester(size = (200, 300))
    2333                  edit_procedure(parent=app.frame)
    2334          #================================================================
    2335
    2336          if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
    2337
    2338                  gmI18N.activate_locale()
    2339                  gmI18N.install_domain()
    2340                  gmDateTime.init()
    2341
    2342                  # obtain patient
    2343                  pat = gmPersonSearch.ask_for_patient()
    2344                  if pat is None:
    2345                          print "No patient. Exiting gracefully..."
    2346                          sys.exit(0)
    2347                  gmPatSearchWidgets.set_active_patient(patient=pat)
    2348
    2349  #       try:
    2350                  # lauch emr dialogs test application
    2351  #               app = testapp(0)
    2352  #               app.MainLoop()
    2353  #       except StandardError:
    2354  #               _log.exception("unhandled exception caught !")
    2355                  # but re-raise them
    2356  #               raise
    2357
    2358                  #test_encounter_edit_area_panel()
    2359                  #test_encounter_edit_area_dialog()
    2360                  #test_epsiode_edit_area_pnl()
    2361                  #test_episode_edit_area_dialog()
    2362                  #test_health_issue_edit_area_dlg()
    2363                  #test_episode_selection_prw()
    2364                  #test_hospital_stay_prw()
    2365                  test_edit_procedure()
    2366
    2367  #================================================================
    2368