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

# Source Code for Module Gnumed.wxpython.gmProgressNotesEAWidgets

      1  # -*- coding: utf-8 -*-
      2  """GNUmed expando based textual progress notes handling widgets."""
      3  #================================================================
      4  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
      5  __license__ = "GPL v2 or later (details at http://www.gnu.org)"
      6
      7  import sys
      8  import logging
      9
     10
     11  import wx
     12
     13
     14  if __name__ == '__main__':
     15          sys.path.insert(0, '../../')
     16
     17  from Gnumed.pycommon import gmI18N
     18
     19  if __name__ == '__main__':
     20          gmI18N.activate_locale()
     21          gmI18N.install_domain()
     22
     23  from Gnumed.pycommon import gmDispatcher
     24  from Gnumed.pycommon import gmDateTime
     25  from Gnumed.pycommon import gmCfg
     26
     27  from Gnumed.business import gmPerson
     28  from Gnumed.business import gmPraxis
     29  from Gnumed.business import gmEMRStructItems
     30  from Gnumed.business import gmClinNarrative
     31
     32  from Gnumed.wxpython import gmGuiHelpers
     33  from Gnumed.wxpython import gmTextCtrl
     34  from Gnumed.wxpython import gmVisualProgressNoteWidgets
     35  from Gnumed.wxpython import gmEMRStructWidgets
     36
     37  _log = logging.getLogger('gm.ui')
     38
     39  #============================================================
     40  from Gnumed.wxGladeWidgets import wxgProgressNotesEAPnl
     41
     42 -class cProgressNotesEAPnl(gmTextCtrl.cExpandoTextCtrlHandling_PanelMixin, wxgProgressNotesEAPnl.wxgProgressNotesEAPnl):
     43          """An Edit Area like panel for entering progress notes.
     44
     45          (
     46                  Subjective:                                     Codes:
     47                          expando text ctrl
     48                  Objective:                                      Codes:
     49                          expando text ctrl
     50                  Assessment:                                     Codes:
     51                          expando text ctrl
     52                  Plan:                                           Codes:
     53                          expando text ctrl
     54          )
     55                  OR
     56          SOAP editor (StyledTextCtrl)
     57                  AND
     58          visual progress notes (panel with images)
     59                  AND
     60          Episode synopsis:                       Codes:
     61                  expando text ctrl
     62
     63          - knows the problem this edit area is about
     64          - can deal with issue or episode type problems
     65          """
     66 -        def __init__(self, *args, **kwargs):
     67
     68                  try:
     69                          self.problem = kwargs['problem']
     70                          del kwargs['problem']
     71                  except KeyError:
     72                          self.problem = None
     73
     74                  wxgProgressNotesEAPnl.wxgProgressNotesEAPnl.__init__(self, *args, **kwargs)
     75
     76                  dbcfg = gmCfg.cCfgSQL()
     77                  self.__use_soap_fields = bool(dbcfg.get2 (
     78                          option = 'horstspace.soap_editor.use_one_field_per_soap_category',
     79                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
     80                          bias = 'user',
     81                          default = True
     82                  ))
     83
     84                  self.__soap_fields = [
     85                          self._TCTRL_Soap,
     86                          self._TCTRL_sOap,
     87                          self._TCTRL_soAp,
     88                          self._TCTRL_soaP
     89                  ]
     90
     91                  self.__init_ui()
     92                  self.__register_interests()
     93
     94                  return
     95
     96          #--------------------------------------------------------
     97 -        def __init_ui(self):
     98                  if self.__use_soap_fields is False:
     99                          for field in self.__soap_fields:
    100                                  field.Hide()
    101                          self._LBL_Soap.Hide()
    102                          self._PRW_Soap_codes.Hide()
    103                          self._LBL_sOap.Hide()
    104                          self._PRW_sOap_codes.Hide()
    105                          self._LBL_soAp.Hide()
    106                          self._PRW_soAp_codes.Hide()
    107                          self._LBL_soaP.Hide()
    108                          self._PRW_soaP_codes.Hide()
    109                          self._STC_soap.Show()
    110
    111                  self.refresh_summary()
    112                  if self.problem is not None:
    113                          if self.problem['summary'] is None:
    114                                  self._TCTRL_episode_summary.SetValue('')
    115                  self.refresh_visual_soap()
    116
    117          #--------------------------------------------------------
    118 -        def refresh(self):
    119                  self.refresh_summary()
    120                  self.refresh_visual_soap()
    121
    122          #--------------------------------------------------------
    123 -        def refresh_summary(self):
    124                  self._TCTRL_episode_summary.SetValue('')
    125                  self._PRW_episode_codes.SetText('', self._PRW_episode_codes.list2data_dict([]))
    126                  self._LBL_summary.SetLabel(_('Episode synopsis'))
    127
    128                  # new problem ?
    129                  if self.problem is None:
    130                          return
    131
    132                  # issue-level problem ?
    133                  if self.problem['type'] == 'issue':
    134                          return
    135
    136                  # episode-level problem
    137                  caption = _('Synopsis (%s)') % (
    138                          gmDateTime.pydt_strftime (
    139                                  self.problem['modified_when'],
    140                                  format = '%B %Y',
    141                                  accuracy = gmDateTime.acc_days
    142                          )
    143                  )
    144                  self._LBL_summary.SetLabel(caption)
    145
    146                  if self.problem['summary'] is not None:
    147                          self._TCTRL_episode_summary.SetValue(self.problem['summary'].strip())
    148
    149                  val, data = self._PRW_episode_codes.generic_linked_codes2item_dict(self.problem.generic_codes)
    150                  self._PRW_episode_codes.SetText(val, data)
    151
    152          #--------------------------------------------------------
    153 -        def refresh_visual_soap(self):
    154                  if self.problem is None:
    155                          self._PNL_visual_soap.refresh(document_folder = None)
    156                          return
    157
    158                  if self.problem['type'] == 'issue':
    159                          self._PNL_visual_soap.refresh(document_folder = None)
    160                          return
    161
    162                  if self.problem['type'] == 'episode':
    163                          pat = gmPerson.gmCurrentPatient()
    164                          doc_folder = pat.get_document_folder()
    165                          emr = pat.emr
    166                          self._PNL_visual_soap.refresh (
    167                                  document_folder = doc_folder,
    168                                  episodes = [self.problem['pk_episode']],
    169                                  encounter = emr.active_encounter['pk_encounter']
    170                          )
    171                          return
    172
    173          #--------------------------------------------------------
    174 -        def clear(self):
    175                  self._TCTRL_episode_summary.SetValue('')
    176                  self._LBL_summary.SetLabel(_('Episode synopsis'))
    177                  self._PRW_episode_codes.SetText('', self._PRW_episode_codes.list2data_dict([]))
    178                  self._PNL_visual_soap.clear()
    179
    180                  if self.__use_soap_fields:
    181                          for field in self.__soap_fields:
    182                                  field.SetValue('')
    183                  else:
    184                          self._STC_soap.SetText_from_SOAP()
    185
    186          #--------------------------------------------------------
    187 -        def add_visual_progress_note(self):
    188                  fname, discard_unmodified = gmVisualProgressNoteWidgets.select_visual_progress_note_template(parent = self)
    189                  if fname is None:
    190                          return False
    191
    192                  if self.problem is None:
    193                          issue = None
    194                          episode = None
    195                  elif self.problem['type'] == 'issue':
    196                          issue = self.problem['pk_health_issue']
    197                          episode = None
    198                  else:
    199                          issue = self.problem['pk_health_issue']
    200                          episode = gmEMRStructItems.problem2episode(self.problem)
    201
    202                  wx.CallAfter (
    203                          gmVisualProgressNoteWidgets.edit_visual_progress_note,
    204                          filename = fname,
    205                          episode = episode,
    206                          discard_unmodified = discard_unmodified,
    207                          health_issue = issue
    208                  )
    209
    210          #--------------------------------------------------------
    211 -        def save(self, emr=None, episode_name_candidates=None, encounter=None):
    212
    213                  if self.empty:
    214                          return True
    215
    216                  # new episode (standalone=unassociated or new-in-issue)
    217                  if (self.problem is None) or (self.problem['type'] == 'issue'):
    218                          episode = self.__create_new_episode(emr = emr, episode_name_candidates = episode_name_candidates)
    219                          # user cancelled
    220                          if episode is None:
    221                                  return False
    222                  # existing episode
    223                  else:
    224                          episode = emr.problem2episode(self.problem)
    225
    226                  if encounter is None:
    227                          encounter = emr.current_encounter['pk_encounter']
    228
    229                  # set summary but only if not already set above for a
    230                  # newly created episode (either standalone or within
    231                  # a health issue)
    232                  if self.problem is not None:
    233                          if self.problem['type'] == 'episode':
    234                                  episode['summary'] = self._TCTRL_episode_summary.GetValue().strip()
    235                                  episode.save()
    236
    237                  # codes for episode
    238                  episode.generic_codes = [ d['data'] for d in self._PRW_episode_codes.GetData() ]
    239
    240                  gmClinNarrative.create_progress_note (
    241                          soap = self.soap,
    242                          episode_id = episode['pk_episode'],
    243                          encounter_id = encounter
    244                  )
    245
    246                  return True
    247
    248          #--------------------------------------------------------
    249          # internal helpers
    250          #--------------------------------------------------------
    251 -        def __create_new_episode(self, emr=None, episode_name_candidates=None):
    252
    253                  episode_name_candidates.append(self._TCTRL_episode_summary.GetValue().strip())
    254                  for candidate in episode_name_candidates:
    255                          if candidate is None:
    256                                  continue
    257                          epi_name = candidate.strip().replace('\r', '//').replace('\n', '//')
    258                          break
    259
    260                  if self.problem is None:
    261                          msg = _(
    262                                  'Enter a short working name for this new problem\n'
    263                                  '(which will become a new, unassociated episode):\n'
    264                          )
    265                  else:
    266                          issue = emr.problem2issue(self.problem)
    267                          msg = _(
    268                                  'Enter a short working name for this new\n'
    269                                  'episode under the existing health issue\n'
    270                                  '\n'
    271                                  '"%s":\n'
    272                          ) % issue['description']
    273
    274                  dlg = wx.TextEntryDialog (
    275                          self, msg,
    276                          caption = _('Creating problem (episode) to save notelet under ...'),
    277                          value = epi_name,
    278                          style = wx.OK | wx.CANCEL | wx.CENTRE
    279                  )
    280                  decision = dlg.ShowModal()
    281                  if decision != wx.ID_OK:
    282                          return None
    283
    284                  epi_name = dlg.GetValue().strip()
    285                  if epi_name == '':
    286                          gmGuiHelpers.gm_show_error(_('Cannot save a new problem without a name.'), _('saving progress note'))
    287                          return None
    288
    289                  # create episode
    290                  new_episode = emr.add_episode (
    291                          episode_name = epi_name[:45],
    292                          pk_health_issue = None,
    293                          is_open = True,
    294                          allow_dupes = True              # this ensures we get a new episode even if a same-name one already exists
    295                  )
    296                  new_episode['summary'] = self._TCTRL_episode_summary.GetValue().strip()
    297                  new_episode.save()
    298
    299                  if self.problem is not None:
    300                          issue = emr.problem2issue(self.problem)
    301                          if not gmEMRStructWidgets.move_episode_to_issue(episode = new_episode, target_issue = issue, save_to_backend = True):
    302                                  gmGuiHelpers.gm_show_warning (
    303                                          _(
    304                                                  'The new episode:\n'
    305                                                  '\n'
    306                                                  ' "%s"\n'
    307                                                  '\n'
    308                                                  'will remain unassociated despite the editor\n'
    309                                                  'having been invoked from the health issue:\n'
    310                                                  '\n'
    311                                                  ' "%s"'
    312                                          ) % (
    313                                                  new_episode['description'],
    314                                                  issue['description']
    315                                          ),
    316                                          _('saving progress note')
    317                                  )
    318
    319                  return new_episode
    320
    321          #--------------------------------------------------------
    322          # event handling
    323          #--------------------------------------------------------
    324 -        def __register_interests(self):
    325                  if self.__use_soap_fields:
    326                          for field in self.__soap_fields:
    327                                  self.bind_expando_layout_event(field)
    328                  self.bind_expando_layout_event(self._TCTRL_episode_summary)
    329                  gmDispatcher.connect(signal = 'blobs.doc_obj_mod_db', receiver = self.refresh_visual_soap)
    330
    331          #--------------------------------------------------------
    332          # properties
    333          #--------------------------------------------------------
    334 -        def _get_soap(self):
    335                  if not self.__use_soap_fields:
    336                          return self._STC_soap.soap
    337
    338                  soap = {}
    339                  tmp = self._TCTRL_Soap.GetValue().strip()
    340                  if tmp != '':
    341                          soap['s'] = [tmp]
    342                  tmp = self._TCTRL_sOap.GetValue().strip()
    343                  if tmp != '':
    344                          soap['o'] = [tmp]
    345                  tmp = self._TCTRL_soAp.GetValue().strip()
    346                  if tmp != '':
    347                          soap['a'] = [tmp]
    348                  tmp = self._TCTRL_soaP.GetValue().strip()
    349                  if tmp != '':
    350                          soap['p'] = [tmp]
    351                  return soap
    352
    353          soap = property(_get_soap, lambda x:x)
    354          #--------------------------------------------------------
    355 -        def _get_empty(self):
    356                  if self.__use_soap_fields:
    357                          for field in self.__soap_fields:
    358                                  if field.GetValue().strip() != '':
    359                                          return False
    360                  else:
    361                          if not self._STC_soap.empty:
    362                                  return False
    363
    364                  # summary
    365                  summary = self._TCTRL_episode_summary.GetValue().strip()
    366                  if self.problem is None:
    367                          if summary != '':
    368                                  return False
    369                  elif self.problem['type'] == 'issue':
    370                          if summary != '':
    371                                  return False
    372                  else:
    373                          if self.problem['summary'] is None:
    374                                  if summary != '':
    375                                          return False
    376                          else:
    377                                  if summary != self.problem['summary'].strip():
    378                                          return False
    379
    380                  # codes
    381                  new_codes = self._PRW_episode_codes.GetData()
    382                  if self.problem is None:
    383                          if len(new_codes) > 0:
    384                                  return False
    385                  elif self.problem['type'] == 'issue':
    386                          if len(new_codes) > 0:
    387                                  return False
    388                  else:
    389                          old_code_pks = self.problem.generic_codes
    390                          if len(old_code_pks) != len(new_codes):
    391                                  return False
    392                          for code in new_codes:
    393                                  if code['data'] not in old_code_pks:
    394                                          return False
    395
    396                  return True
    397
    398          empty = property(_get_empty, lambda x:x)
    399
    400  #============================================================
    401  # main
    402  #---------------------------------------------------
    403  if __name__ == '__main__':
    404
    405          if len(sys.argv) < 2:
    406                  sys.exit()
    407
    408          if sys.argv[1] != 'test':
    409                  sys.exit()
    410
    411          #----------------------------------------
    412 -        def test_cProgressNotesEAPnl():
    413                  pat = gmPersonSearch.ask_for_patient()
    414                  application = wx.PyWidgetTester(size=(800,500))
    415                  soap_input = cProgressNotesEAPnl(application.frame, -1)
    416                  application.frame.Show(True)
    417                  application.MainLoop()
    418          #----------------------------------------
    419
    420          test_cProgressNotesEAPnl()
    421