Gnumed.wxpython.gmProgressNotesEAWidgets

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                          parent = self,
    276                          message = msg,
    277                          caption = _('Creating problem (episode) to save notelet under ...'),
    278                          defaultValue = epi_name,
    279                          style = wx.OK | wx.CANCEL | wx.CENTRE
    280                  )
    281                  decision = dlg.ShowModal()
    282                  if decision != wx.ID_OK:
    283                          return None
    284
    285                  epi_name = dlg.GetValue().strip()
    286                  if epi_name == '':
    287                          gmGuiHelpers.gm_show_error(_('Cannot save a new problem without a name.'), _('saving progress note'))
    288                          return None
    289
    290                  # create episode
    291                  new_episode = emr.add_episode (
    292                          episode_name = epi_name[:45],
    293                          pk_health_issue = None,
    294                          is_open = True,
    295                          allow_dupes = True              # this ensures we get a new episode even if a same-name one already exists
    296                  )
    297                  new_episode['summary'] = self._TCTRL_episode_summary.GetValue().strip()
    298                  new_episode.save()
    299
    300                  if self.problem is not None:
    301                          issue = emr.problem2issue(self.problem)
    302                          if not gmEMRStructWidgets.move_episode_to_issue(episode = new_episode, target_issue = issue, save_to_backend = True):
    303                                  gmGuiHelpers.gm_show_warning (
    304                                          _(
    305                                                  'The new episode:\n'
    306                                                  '\n'
    307                                                  ' "%s"\n'
    308                                                  '\n'
    309                                                  'will remain unassociated despite the editor\n'
    310                                                  'having been invoked from the health issue:\n'
    311                                                  '\n'
    312                                                  ' "%s"'
    313                                          ) % (
    314                                                  new_episode['description'],
    315                                                  issue['description']
    316                                          ),
    317                                          _('saving progress note')
    318                                  )
    319
    320                  return new_episode
    321
    322          #--------------------------------------------------------
    323          # event handling
    324          #--------------------------------------------------------
    325 -        def __register_interests(self):
    326                  if self.__use_soap_fields:
    327                          for field in self.__soap_fields:
    328                                  self.bind_expando_layout_event(field)
    329                  self.bind_expando_layout_event(self._TCTRL_episode_summary)
    330                  gmDispatcher.connect(signal = 'blobs.doc_obj_mod_db', receiver = self.refresh_visual_soap)
    331
    332          #--------------------------------------------------------
    333          # properties
    334          #--------------------------------------------------------
    335 -        def _get_soap(self):
    336                  if not self.__use_soap_fields:
    337                          return self._STC_soap.soap
    338
    339                  soap = {}
    340                  tmp = self._TCTRL_Soap.GetValue().strip()
    341                  if tmp != '':
    342                          soap['s'] = [tmp]
    343                  tmp = self._TCTRL_sOap.GetValue().strip()
    344                  if tmp != '':
    345                          soap['o'] = [tmp]
    346                  tmp = self._TCTRL_soAp.GetValue().strip()
    347                  if tmp != '':
    348                          soap['a'] = [tmp]
    349                  tmp = self._TCTRL_soaP.GetValue().strip()
    350                  if tmp != '':
    351                          soap['p'] = [tmp]
    352                  return soap
    353
    354          soap = property(_get_soap, lambda x:x)
    355          #--------------------------------------------------------
    356 -        def _get_empty(self):
    357                  if self.__use_soap_fields:
    358                          for field in self.__soap_fields:
    359                                  if field.GetValue().strip() != '':
    360                                          return False
    361                  else:
    362                          if not self._STC_soap.empty:
    363                                  return False
    364
    365                  # summary
    366                  summary = self._TCTRL_episode_summary.GetValue().strip()
    367                  if self.problem is None:
    368                          if summary != '':
    369                                  return False
    370                  elif self.problem['type'] == 'issue':
    371                          if summary != '':
    372                                  return False
    373                  else:
    374                          if self.problem['summary'] is None:
    375                                  if summary != '':
    376                                          return False
    377                          else:
    378                                  if summary != self.problem['summary'].strip():
    379                                          return False
    380
    381                  # codes
    382                  new_codes = self._PRW_episode_codes.GetData()
    383                  if self.problem is None:
    384                          if len(new_codes) > 0:
    385                                  return False
    386                  elif self.problem['type'] == 'issue':
    387                          if len(new_codes) > 0:
    388                                  return False
    389                  else:
    390                          old_code_pks = self.problem.generic_codes
    391                          if len(old_code_pks) != len(new_codes):
    392                                  return False
    393                          for code in new_codes:
    394                                  if code['data'] not in old_code_pks:
    395                                          return False
    396
    397                  return True
    398
    399          empty = property(_get_empty, lambda x:x)
    400
    401  #============================================================
    402  # main
    403  #---------------------------------------------------
    404  if __name__ == '__main__':
    405
    406          if len(sys.argv) < 2:
    407                  sys.exit()
    408
    409          if sys.argv[1] != 'test':
    410                  sys.exit()
    411
    412          #----------------------------------------
    413 -        def test_cProgressNotesEAPnl():
    414                  pat = gmPersonSearch.ask_for_patient()
    415                  application = wx.PyWidgetTester(size=(800,500))
    416                  soap_input = cProgressNotesEAPnl(application.frame, -1)
    417                  application.frame.Show(True)
    418                  application.MainLoop()
    419          #----------------------------------------
    420
    421          test_cProgressNotesEAPnl()
    422