Gnumed.wxpython.gmExportAreaWidgets

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

# Source Code for Module Gnumed.wxpython.gmExportAreaWidgets

       1  """GNUmed patient export area widgets."""
       2  #================================================================
       3  __author__ = "Karsten.Hilbert@gmx.net"
       4  __license__ = "GPL v2 or later"
       5
       6  # std lib
       7  import sys
       8  import logging
       9  import os.path
      10  import shutil
      11
      12
      13  # 3rd party
      14  import wx
      15
      16
      17  # GNUmed libs
      18  if __name__ == '__main__':
      19          sys.path.insert(0, '../../')
      20
      21  from Gnumed.pycommon import gmDispatcher
      22  from Gnumed.pycommon import gmTools
      23  from Gnumed.pycommon import gmMimeLib
      24  from Gnumed.pycommon import gmDateTime
      25  from Gnumed.pycommon import gmPrinting
      26  from Gnumed.pycommon import gmShellAPI
      27  from Gnumed.pycommon import gmNetworkTools
      28  from Gnumed.pycommon import gmCfg2
      29  from Gnumed.pycommon import gmLog2
      30
      31  from Gnumed.business import gmPerson
      32  from Gnumed.business import gmExportArea
      33  from Gnumed.business import gmPraxis
      34
      35  from Gnumed.wxpython import gmRegetMixin
      36  from Gnumed.wxpython import gmGuiHelpers
      37  from Gnumed.wxpython import gmDocumentWidgets
      38
      39
      40  _log = logging.getLogger('gm.ui')
      41  _cfg = gmCfg2.gmCfgData()
      42
      43
      44  #============================================================
      45  from Gnumed.wxGladeWidgets import wxgCreatePatientMediaDlg
      46
      47 -class cCreatePatientMediaDlg(wxgCreatePatientMediaDlg.wxgCreatePatientMediaDlg):
      48
      49 -        def __init__(self, *args, **kwargs):
      50                  self.__burn2cd = False
      51                  try:
      52                          self.__burn2cd = kwargs['burn2cd']
      53                          del kwargs['burn2cd']
      54                  except KeyError:
      55                          pass
      56                  if self.__burn2cd:
      57                          _log.debug('planning to burn export area items to CD/DVD')
      58                  else:
      59                          _log.debug('planning to save export area items to disk')
      60                  self.__patient = kwargs['patient']
      61                  del kwargs['patient']
      62                  self.__item_count = kwargs['item_count']
      63                  del kwargs['item_count']
      64                  wxgCreatePatientMediaDlg.wxgCreatePatientMediaDlg.__init__(self, *args, **kwargs)
      65
      66                  self.__init_ui()
      67
      68          #--------------------------------------------------------
      69          # event handling
      70          #--------------------------------------------------------
      71 -        def _on_select_directory_button_pressed(self, event):
      72                  event.Skip()
      73                  if self.__burn2cd:
      74                          msg = _('Select a directory for inclusion into the patient CD / DVD.')
      75                  else:
      76                          msg = _('Select a directory in which to create the patient media.')
      77                  def_path = self._LBL_directory.Label
      78                  dlg = wx.DirDialog (
      79                          self,
      80                          message = msg,
      81                          defaultPath = def_path
      82                  )
      83                  choice = dlg.ShowModal()
      84                  path = dlg.GetPath()
      85                  dlg.Destroy()
      86                  if choice != wx.ID_OK:
      87                          return
      88                  self._LBL_directory.Label = path
      89                  self.__refresh_dir_is_empty()
      90                  self.__refresh_include_or_remove_existing_data()
      91
      92          #--------------------------------------------------------
      93 -        def _on_use_subdirectory_changed(self, event):
      94                  event.Skip()
      95
      96                  self.__refresh_include_or_remove_existing_data()
      97
      98                  if self._CHBOX_use_subdirectory.IsChecked():
      99                          self._LBL_subdirectory.Label = '%s/%s-###' % (
     100                                  self._LBL_directory.Label,
     101                                  self.__patient.subdir_name
     102                          )
     103                          return
     104
     105                  self._LBL_subdirectory.Label = ''
     106
     107          #--------------------------------------------------------
     108 -        def _on_save_button_pressed(self, event):
     109                  event.Skip()
     110
     111                  if self.__burn2cd:
     112                          self.EndModal(wx.ID_SAVE)
     113                          return
     114
     115                  if self._CHBOX_use_subdirectory.IsChecked() is True:
     116                          self.EndModal(wx.ID_SAVE)
     117                          return
     118
     119                  path = self._LBL_directory.Label
     120
     121                  if gmTools.dir_is_empty(path) is True:
     122                          self.EndModal(wx.ID_SAVE)
     123                          return
     124
     125                  if self._RBTN_remove_data.Value is True:
     126                          really_remove_existing_data = gmGuiHelpers.gm_show_question (
     127                                  title = _('Creating patient media'),
     128                                  question = _(
     129                                          'Really delete any existing data under\n'
     130                                          '\n'
     131                                          ' [%s]\n'
     132                                          '\n'
     133                                          'from disk ?\n'
     134                                          '\n'
     135                                          '(this operation is generally not reversible)'
     136                                  ) % path
     137                          )
     138                          if really_remove_existing_data is False:
     139                                  return
     140
     141                  self.EndModal(wx.ID_SAVE)
     142
     143          #--------------------------------------------------------
     144 -        def _on_browse_directory_button_pressed(self, event):
     145                  event.Skip()
     146                  path = self._LBL_directory.Label.strip()
     147                  if path == '':
     148                          return
     149                  gmMimeLib.call_viewer_on_file(path, block = False)
     150
     151          #--------------------------------------------------------
     152          # internal API
     153          #--------------------------------------------------------
     154 -        def __init_ui(self):
     155
     156                  self._LBL_dir_is_empty.Label = ''
     157                  self._LBL_subdirectory.Label = ''
     158
     159                  if self.__burn2cd:
     160                          self._LBL_existing_data.Hide()
     161                          self._BTN_browse_directory.Disable()
     162                          self._RBTN_include_data.Hide()
     163                          self._RBTN_remove_data.Hide()
     164                          self._CHBOX_include_directory.Show()
     165                          self._CHBOX_use_subdirectory.Hide()
     166                          self._LBL_subdirectory.Hide()
     167                          self._CHBOX_generate_metadata.Hide()
     168                          lines = [
     169                                  _('Preparing patient media for burning onto CD / DVD'),
     170                                  ''
     171                          ]
     172                          found, external_cmd = gmShellAPI.detect_external_binary('gm-burn_doc')
     173                          if not found:
     174                                  lines.append(_('Script <gm-burn_doc(.bat)> not found.'))
     175                                  lines.append('')
     176                                  lines.append(_('Cannot attempt to burn patient media onto CD/DVD.'))
     177                                  self._BTN_save.Disable()
     178                          else:
     179                                  lines.append(_('Patient: %s') % self.__patient['description_gender'])
     180                                  lines.append('')
     181                                  lines.append(_('Number of items to export onto CD/DVD: %s\n') % self.__item_count)
     182                          self._LBL_header.Label = '\n'.join(lines)
     183                          return
     184
     185                  lines = [
     186                          _('Preparing patient media for saving to disk (USB, harddrive).'),
     187                          '',
     188                          _('Patient: %s') % self.__patient['description_gender'],
     189                          '',
     190                          _('Number of items to export to disk: %s\n') % self.__item_count
     191                  ]
     192                  self._LBL_header.Label = '\n'.join(lines)
     193                  self._LBL_directory.Label = os.path.join(gmTools.gmPaths().home_dir, 'gnumed')
     194                  self.__refresh_dir_is_empty()
     195
     196          #--------------------------------------------------------
     197 -        def __refresh_dir_is_empty(self):
     198                  path = self._LBL_directory.Label.strip()
     199                  if path == '':
     200                          self._LBL_dir_is_empty.Label = ''
     201                          self._BTN_browse_directory.Disable()
     202                          self._CHBOX_include_directory.Disable()
     203                          return
     204                  is_empty = gmTools.dir_is_empty(directory = path)
     205                  if is_empty is None:
     206                          self._LBL_dir_is_empty.Label = _('(cannot check directory)')
     207                          self._BTN_browse_directory.Disable()
     208                          self._CHBOX_include_directory.Disable()
     209                          return
     210                  if is_empty is True:
     211                          self._LBL_dir_is_empty.Label = _('(directory appears empty)')
     212                          self._BTN_browse_directory.Disable()
     213                          self._CHBOX_include_directory.Disable()
     214                          return
     215
     216                  msg = _('directory already contains data')
     217                  self._BTN_browse_directory.Enable()
     218                  self._CHBOX_include_directory.Enable()
     219
     220                  if os.path.isfile(os.path.join(path, 'DICOMDIR')):
     221                          msg = _('%s\n- DICOM data') % msg
     222
     223                  if os.path.isdir(os.path.join(path, 'documents')):
     224                          if len(os.listdir(os.path.join(path, 'documents'))) > 0:
     225                                  msg = _('%s\n- additional documents') % msg
     226
     227                  self._LBL_dir_is_empty.Label = msg
     228                  self.Layout()
     229
     230          #--------------------------------------------------------
     231 -        def __refresh_include_or_remove_existing_data(self):
     232                  if self._CHBOX_use_subdirectory.IsChecked():
     233                          self._RBTN_include_data.Disable()
     234                          self._RBTN_remove_data.Disable()
     235                          return
     236
     237                  path = self._LBL_directory.Label.strip()
     238                  if path == '':
     239                          self._RBTN_include_data.Disable()
     240                          self._RBTN_remove_data.Disable()
     241                          return
     242
     243                  is_empty = gmTools.dir_is_empty(directory = path)
     244                  if is_empty is None:
     245                          self._RBTN_include_data.Disable()
     246                          self._RBTN_remove_data.Disable()
     247                          return
     248
     249                  if is_empty is True:
     250                          self._RBTN_include_data.Disable()
     251                          self._RBTN_remove_data.Disable()
     252                          return
     253
     254                  self._RBTN_include_data.Enable()
     255                  self._RBTN_remove_data.Enable()
     256
     257  #============================================================
     258  from Gnumed.wxGladeWidgets import wxgExportAreaPluginPnl
     259
     260 -class cExportAreaPluginPnl(wxgExportAreaPluginPnl.wxgExportAreaPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
     261          """Panel holding a number of items for further processing.
     262
     263          Acts on the current patient.
     264
     265          Used as notebook page."""
     266 -        def __init__(self, *args, **kwargs):
     267                  wxgExportAreaPluginPnl.wxgExportAreaPluginPnl.__init__(self, *args, **kwargs)
     268                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
     269                  self.__init_ui()
     270                  self.__register_interests()
     271
     272          #--------------------------------------------------------
     273          # event handling
     274          #--------------------------------------------------------
     275 -        def __register_interests(self):
     276                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
     277  #               gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget)
     278                  gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
     279
     280          #--------------------------------------------------------
     281 -        def _on_pre_patient_unselection(self):
     282                  self._LCTRL_items.set_string_items([])
     283
     284          #--------------------------------------------------------
     285 -        def _on_table_mod(self, *args, **kwargs):
     286                  if kwargs['table'] != 'clin.export_item':
     287                          return
     288                  pat = gmPerson.gmCurrentPatient()
     289                  if not pat.connected:
     290                          return
     291                  if kwargs['pk_identity'] != pat.ID:
     292                          return
     293                  self._schedule_data_reget()
     294
     295          #--------------------------------------------------------
     296 -        def _on_list_item_selected(self, event):
     297                  event.Skip()
     298
     299          #--------------------------------------------------------
     300 -        def _on_show_item_button_pressed(self, event):
     301                  event.Skip()
     302                  item = self._LCTRL_items.get_selected_item_data(only_one = True)
     303                  if item is None:
     304                          return
     305                  item.display_via_mime(block = False)
     306
     307          #--------------------------------------------------------
     308 -        def _on_add_items_button_pressed(self, event):
     309                  event.Skip()
     310                  dlg = wx.FileDialog (
     311                          parent = self,
     312                          message = _("Select files to add to the export area"),
     313                          defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')),
     314                          style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE | wx.FD_PREVIEW
     315                  )
     316                  choice = dlg.ShowModal()
     317                  fnames = dlg.GetPaths()
     318                  dlg.Destroy()
     319                  if choice != wx.ID_OK:
     320                          return
     321                  if not gmPerson.gmCurrentPatient().export_area.add_files(fnames):
     322                          gmGuiHelpers.gm_show_error (
     323                                  title = _('Adding files to export area'),
     324                                  error = _('Cannot add (some of) the following files to the export area:\n%s ') % '\n '.join(fnames)
     325                          )
     326
     327          #--------------------------------------------------------
     328 -        def _on_add_from_archive_button_pressed(self, event):
     329                  event.Skip()
     330                  selected_docs = gmDocumentWidgets.manage_documents (
     331                          parent = self,
     332                          msg = _('Select the documents to be put into the export area:'),
     333                          single_selection = False
     334                  )
     335                  if selected_docs is None:
     336                          return
     337                  gmPerson.gmCurrentPatient().export_area.add_documents(documents = selected_docs)
     338
     339          #--------------------------------------------------------
     340 -        def _on_clipboard_items_button_pressed(self, event):
     341                  event.Skip()
     342                  clip = gmGuiHelpers.clipboard2file(check_for_filename = True)
     343                  if clip is None:
     344                          return
     345                  if clip is False:
     346                          return
     347                  if not gmPerson.gmCurrentPatient().export_area.add_file(filename = clip, hint = _('clipboard')):
     348                          gmGuiHelpers.gm_show_error (
     349                                  title = _('Loading clipboard item (saved to file) into export area'),
     350                                  error = _('Cannot add the following clip to the export area:\n%s ') % clip
     351                          )
     352
     353          #--------------------------------------------------------
     354 -        def _on_scan_items_button_pressed(self, event):
     355                  event.Skip()
     356                  scans = gmDocumentWidgets.acquire_images_from_capture_device(calling_window = self)
     357                  if scans is None:
     358                          return
     359
     360                  if not gmPerson.gmCurrentPatient().export_area.add_files(scans, _('scan')):
     361                          gmGuiHelpers.gm_show_error (
     362                                  title = _('Scanning files into export area'),
     363                                  error = _('Cannot add (some of) the following scans to the export area:\n%s ') % '\n '.join(fnames)
     364                          )
     365
     366          #--------------------------------------------------------
     367 -        def _on_remove_items_button_pressed(self, event):
     368                  event.Skip()
     369                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     370                  if len(items) == 0:
     371                          return
     372                  really_delete = gmGuiHelpers.gm_show_question (
     373                          title = _('Deleting document from export area.'),
     374                          question = _('Really remove %s selected document(s)\nfrom the patient export area ?') % len(items)
     375                  )
     376                  if not really_delete:
     377                          return
     378                  for item in items:
     379                          gmExportArea.delete_export_item(pk_export_item = item['pk_export_item'])
     380
     381          #--------------------------------------------------------
     382 -        def _on_print_items_button_pressed(self, event):
     383                  event.Skip()
     384                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     385                  if len(items) == 0:
     386                          return
     387
     388                  files2print = []
     389                  for item in items:
     390                          files2print.append(item.save_to_file())
     391
     392                  if len(files2print) == 0:
     393                          return
     394
     395                  jobtype = 'export_area'
     396                  printed = gmPrinting.print_files(filenames = files2print, jobtype = jobtype, verbose = _cfg.get(option = 'debug'))
     397                  if not printed:
     398                          gmGuiHelpers.gm_show_error (
     399                                  aMessage = _('Error printing documents.'),
     400                                  aTitle = _('Printing [%s]') % jobtype
     401                          )
     402                          return False
     403
     404                  self.__save_soap_note(soap = _('Printed:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     405                  return True
     406
     407          #--------------------------------------------------------
     408 -        def _on_remote_print_button_pressed(self, event):
     409                  event.Skip()
     410                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     411                  for item in items:
     412                          item.is_print_job = True
     413
     414          #--------------------------------------------------------
     415 -        def _on_save_items_button_pressed(self, event):
     416                  event.Skip()
     417
     418                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     419                  if len(items) == 0:
     420                          items = self._LCTRL_items.get_item_data()
     421
     422                  if len(items) == 0:
     423                          return
     424
     425                  pat = gmPerson.gmCurrentPatient()
     426                  dlg = cCreatePatientMediaDlg (self, -1, burn2cd = False, patient = pat, item_count = len(items))
     427                  _log.debug("calling dlg.ShowModal()")
     428                  choice = dlg.ShowModal()
     429                  _log.debug("after returning from dlg.ShowModal()")
     430                  if choice != wx.ID_SAVE:
     431                          dlg.Destroy()
     432                          return
     433
     434                  use_subdir = dlg._CHBOX_use_subdirectory.IsChecked()
     435                  path = dlg._LBL_directory.Label.strip()
     436                  remove_existing_data = dlg._RBTN_remove_data.Value is True
     437                  generate_metadata = dlg._CHBOX_generate_metadata.IsChecked()
     438                  dlg.Destroy()
     439                  if use_subdir:
     440                          path = gmTools.mk_sandbox_dir (
     441                                  prefix = '%s-' % pat.subdir_name,
     442                                  base_dir = path
     443                          )
     444                  else:
     445                          if remove_existing_data is True:
     446                                  if gmTools.rm_dir_content(path) is False:
     447                                          gmGuiHelpers.gm_show_error (
     448                                                  title = _('Creating patient media'),
     449                                                  error = _('Cannot remove content from\n [%s]') % path
     450                                          )
     451                                          return False
     452
     453                  exp_area = pat.export_area
     454                  if generate_metadata:
     455                          export_dir = exp_area.export(base_dir = path, items = items)
     456                  else:
     457                          export_dir = exp_area.dump_items_to_disk(base_dir = path, items = items)
     458
     459                  self.__save_soap_note(soap = _('Saved to [%s]:\n - %s') % (
     460                          export_dir,
     461                          '\n - '.join([ i['description'] for i in items ])
     462                  ))
     463
     464                  msg = _('Saved documents into directory:\n\n %s') % export_dir
     465                  browse_index = gmGuiHelpers.gm_show_question (
     466                          title = _('Creating patient media'),
     467                          question = msg + '\n\n' + _('Browse patient data pack ?'),
     468                          cancel_button = False
     469                  )
     470                  if browse_index:
     471                          if generate_metadata:
     472                                  gmNetworkTools.open_url_in_browser(url = 'file://%s' % os.path.join(export_dir, 'index.html'))
     473                          else:
     474                                  gmMimeLib.call_viewer_on_file(export_dir, block = False)
     475
     476                  return True
     477
     478          #--------------------------------------------------------
     479 -        def _on_burn_items_button_pressed(self, event):
     480                  event.Skip()
     481
     482                  # anything to do ?
     483                  found, external_cmd = gmShellAPI.detect_external_binary('gm-burn_doc')
     484                  if not found:
     485                          return
     486                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     487                  if len(items) == 0:
     488                          items = self._LCTRL_items.get_item_data()
     489                  if len(items) == 0:
     490                          return
     491
     492                  pat = gmPerson.gmCurrentPatient()
     493                  dlg = cCreatePatientMediaDlg(self, -1, burn2cd = True, patient = pat, item_count = len(items))
     494                  choice = dlg.ShowModal()
     495                  if choice != wx.ID_SAVE:
     496                          return
     497                  path2include = dlg._LBL_directory.Label.strip()
     498                  include_selected_dir = dlg._CHBOX_include_directory.IsChecked()
     499                  dlg.Destroy()
     500
     501                  # do the export
     502                  base_dir = None
     503                  if include_selected_dir:
     504                          if gmTools.dir_is_empty(path2include) is False:
     505                                  base_dir = gmTools.get_unique_filename(suffix = '.iso')
     506                                  try:
     507                                          shutil.copytree(path2include, base_dir)
     508                                  except shutil.Error:
     509                                          _log.exception('cannot copy include directory [%s] -> [%s]', path2include, base_dir)
     510                                          return
     511
     512                  export_dir = gmPerson.gmCurrentPatient().export_area.export(base_dir = base_dir, items = items)
     513                  if export_dir is None:
     514                          return
     515
     516                  # burn onto media
     517                  cmd = '%s %s' % (external_cmd, export_dir)
     518                  if os.name == 'nt':
     519                          blocking = True
     520                  else:
     521                          blocking = False
     522                  success = gmShellAPI.run_command_in_shell (
     523                          command = cmd,
     524                          blocking = blocking
     525                  )
     526                  if not success:
     527                          gmGuiHelpers.gm_show_error (
     528                                  aMessage = _('Error burning documents to CD/DVD.'),
     529                                  aTitle = _('Burning documents')
     530                          )
     531                          return
     532
     533                  self.__save_soap_note(soap = _('Burned onto CD/DVD:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     534
     535                  browse_index = gmGuiHelpers.gm_show_question (
     536                          title = _('Creating patient media'),
     537                          question = _('Browse patient data pack ?'),
     538                          cancel_button = False
     539                  )
     540                  if browse_index:
     541                          gmNetworkTools.open_url_in_browser(url = 'file://%s' % os.path.join(export_dir, 'index.html'))
     542
     543                  return True
     544
     545          #--------------------------------------------------------
     546 -        def _on_zip_items_button_pressed(self, event):
     547                  event.Skip()
     548                  # anything to do ?
     549                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     550                  if len(items) == 0:
     551                          items = self._LCTRL_items.get_item_data()
     552                          if len(items) == 0:
     553                                  return None
     554                          # ask, might be a lot
     555                          process_all = gmGuiHelpers.gm_show_question (
     556                                  title = _('Creating zip archive'),
     557                                  question = _('You have not selected any entries.\n\nCreate archive from all %s entries ?') % len(items),
     558                                  cancel_button = False
     559                          )
     560                          if not process_all:
     561                                  return None
     562
     563                  exp_area = gmPerson.gmCurrentPatient().export_area
     564                  # get passphrase from user
     565                  zip_file = exp_area.export_as_zip(passphrase = None)
     566                  if zip_file is None:
     567                          gmGuiHelpers.gm_show_error (
     568                                  aMessage = _('Error creating zip file.'),
     569                                  aTitle = _('Creating zip archive')
     570                          )
     571                          return False
     572
     573                  # cleanup - ask !
     574                  # - files corresponding to DIR/DIR CONTENT entries
     575                  # - entries in export area
     576                  remove_items = gmGuiHelpers.gm_show_question (
     577                          title = _('Creating zip archive'),
     578                          question = _(
     579                                  'Zip archive created as:\n'
     580                                  '\n'
     581                                  ' [%s]\n'
     582                                  '\n'
     583                                  'Remove archived entries from export area ?'
     584                          ) % zip_file,
     585                          cancel_button = False
     586                  )
     587                  if remove_items:
     588                          exp_area.remove_items(items = items)
     589                  return True
     590
     591          #--------------------------------------------------------
     592 -        def _on_archive_items_button_pressed(self, event):
     593                  print("Event handler '_on_archive_items_button_pressed' not implemented!")
     594                  event.Skip()
     595
     596          #--------------------------------------------------------
     597 -        def _on_mail_items_button_pressed(self, event):
     598                  event.Skip()
     599
     600                  found, external_cmd = gmShellAPI.detect_external_binary('gm-mail_doc')
     601                  if not found:
     602                          return False
     603
     604                  zip_file = self.__export_as_zip (
     605                          _('Mailing documents as zip archive'),
     606                          encrypt = True
     607                  )
     608                  if zip_file is None:
     609                          gmGuiHelpers.gm_show_error (
     610                                  aMessage = _('Error creating zip file.'),
     611                                  aTitle = title
     612                          )
     613                          return False
     614
     615                  prax = gmPraxis.gmCurrentPraxisBranch()
     616                  args = [external_cmd, prax.vcf, zip_file]
     617                  success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args,verbose = _cfg.get(option = 'debug'))
     618                  if not success:
     619                          gmGuiHelpers.gm_show_error (
     620                                  aMessage = _('Error mailing documents.'),
     621                                  aTitle = title
     622                          )
     623                          return False
     624
     625                  self.__save_soap_note(soap = _('Mailed:\n - %s') % '\n - '.join([ i['description'] for i in items ]))
     626                  return True
     627
     628          #--------------------------------------------------------
     629 -        def _on_fax_items_button_pressed(self, event):
     630                  event.Skip()
     631
     632                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     633                  if len(items) == 0:
     634                          return
     635
     636                  found, external_cmd = gmShellAPI.detect_external_binary('gm-fax_doc')
     637                  if not found:
     638                          return False
     639
     640                  files2fax = []
     641                  for item in items:
     642                          files2fax.append(item.save_to_file())
     643
     644                  fax_number = wx.GetTextFromUser (
     645                          _('Please enter the fax number here !\n\n'
     646                            'It can be left empty if the external\n'
     647                            'fax software knows how to get the number.'),
     648                          caption = _('Faxing documents'),
     649                          parent = self,
     650                          centre = True
     651                  )
     652
     653                  cmd = '%s "%s" %s' % (external_cmd, fax_number, ' '.join(files2fax))
     654                  if os.name == 'nt':
     655                          blocking = True
     656                  else:
     657                          blocking = False
     658                  success = gmShellAPI.run_command_in_shell (
     659                          command = cmd,
     660                          blocking = blocking
     661                  )
     662                  if not success:
     663                          gmGuiHelpers.gm_show_error (
     664                                  aMessage = _('Error faxing documents to\n\n  %s') % fax_number,
     665                                  aTitle = _('Faxing documents')
     666                          )
     667                          return False
     668
     669                  self.__save_soap_note(soap = _('Faxed to [%s]:\n - %s') % (
     670                          fax_number,
     671                          '\n - '.join([ i['description'] for i in items ])
     672                  ))
     673                  return True
     674
     675          #--------------------------------------------------------
     676 -        def repopulate_ui(self):
     677                  self._populate_with_data()
     678
     679          #--------------------------------------------------------
     680          # internal API
     681          #--------------------------------------------------------
     682 -        def __init_ui(self):
     683                  self._LCTRL_items.set_columns([_('By'), _('When'), _('Description')])
     684
     685                  self._BTN_archive_items.Disable()
     686
     687                  # there's no GetToolTipText() in wx2.8
     688                  self.__mail_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-mail_doc')
     689                  if not self.__mail_script_exists:
     690                          self._BTN_mail_items.Disable()
     691                          tt = self._BTN_mail_items.GetToolTipText() + '\n\n' + _('<gm-mail_doc(.bat) not found>')
     692                          self._BTN_mail_items.SetToolTip(tt)
     693
     694                  self.__fax_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-fax_doc')
     695                  if not self.__fax_script_exists:
     696                          self._BTN_fax_items.Disable()
     697                          tt = self._BTN_fax_items.GetToolTipText() + '\n\n' + _('<gm-fax_doc(.bat) not found>')
     698                          self._BTN_fax_items.SetToolTip(tt)
     699
     700                  self.__burn_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-burn_doc')
     701                  if not self.__burn_script_exists:
     702                          self._BTN_burn_items.Disable()
     703                          tt = self._BTN_burn_items.GetToolTipText() + '\n\n' + _('<gm-burn_doc(.bat) not found>')
     704                          self._BTN_burn_items.SetToolTip(tt)
     705
     706                  # make me and listctrl file drop targets
     707                  dt = gmGuiHelpers.cFileDropTarget(target = self)
     708                  self.SetDropTarget(dt)
     709                  dt = gmGuiHelpers.cFileDropTarget(on_drop_callback = self._drop_target_consume_filenames)
     710                  self._LCTRL_items.SetDropTarget(dt)
     711
     712          #--------------------------------------------------------
     713 -        def __save_soap_note(self, soap=None):
     714                  if soap.strip() == '':
     715                          return
     716                  emr = gmPerson.gmCurrentPatient().emr
     717                  epi = emr.add_episode(episode_name = 'administrative', is_open = False)
     718                  emr.add_clin_narrative (
     719                          soap_cat = None,
     720                          note = soap,
     721                          episode = epi
     722                  )
     723
     724          #--------------------------------------------------------
     725 -        def __export_as_zip(self, msg_title, encrypt=True):
     726                  # anything to do ?
     727                  items = self._LCTRL_items.get_selected_item_data(only_one = False)
     728                  if len(items) == 0:
     729                          items = self._LCTRL_items.get_item_data()
     730                          if len(items) == 0:
     731                                  return None
     732                          # ask, might be a lot
     733                          process_all = gmGuiHelpers.gm_show_question (
     734                                  title = msg_title,
     735                                  question = _('You have not selected any entries.\n\nCreate archive from all %s entries ?') % len(items),
     736                                  cancel_button = False
     737                          )
     738                          if not process_all:
     739                                  return None
     740                  # get password ?
     741                  zip_pwd = None
     742                  if encrypt:
     743                          zip_pwd = self.__get_archive_password(msg_title)
     744                          if zip_pwd is None:
     745                                  _log.debug('user aborted by not providing the same password twice')
     746                                  return None
     747                  # create archive
     748                  exp_area = gmPerson.gmCurrentPatient().export_area
     749                  zip_file = exp_area.export_as_zip(passphrase = zip_pwd)
     750                  if zip_file is None:
     751                          gmGuiHelpers.gm_show_error (
     752                                  aMessage = _('Error creating zip file.'),
     753                                  aTitle = title
     754                          )
     755                          return None
     756                  return zip_file
     757
     758          #--------------------------------------------------------
     759 -        def __get_archive_password(self, msg_title):
     760                  while True:
     761                          zip_pwd = wx.GetPasswordFromUser (
     762                                  message = _(
     763                                          'Enter passphrase to protect the archive with.\n'
     764                                          '\n'
     765                                          '(minimum length: 5, trailing blanks will be stripped)'
     766                                  ),
     767                                  caption = msg_title
     768                          )
     769                          # minimal weakness check
     770                          zip_pwd = zip_pwd.rstrip()
     771                          if len(zip_pwd) > 4:
     772                                  break
     773                          retry = gmGuiHelpers.gm_show_question (
     774                                  title = msg_title,
     775                                  question = _(
     776                                          'Insufficient passphrase.\n'
     777                                          '\n'
     778                                          '(minimum length: 5, trailing blanks will be stripped)\n'
     779                                          '\n'
     780                                          'Enter another passphrase ?'
     781                                  )
     782                          )
     783                          if not retry:
     784                                  # user changed her mind
     785                                  return None
     786                  # confidentiality
     787                  gmLog2.add_word2hide(zip_pwd)
     788                  # reget password
     789                  while True:
     790                          zip_pwd4comparison = wx.GetPasswordFromUser (
     791                                  message = _(
     792                                          'Once more enter passphrase to protect the archive with.\n'
     793                                          '\n'
     794                                          '(this will protect you from typos)\n'
     795                                          '\n'
     796                                          'Abort by leaving empty.'
     797                                  ),
     798                                  caption = msg_title
     799                          )
     800                          zip_pwd4comparison = zip_pwd4comparison.rstrip()
     801                          if zip_pwd4comparison == '':
     802                                  # user changed her mind ...
     803                                  return None
     804                          if zip_pwd == zip_pwd4comparison:
     805                                  break
     806                          gmGuiHelpers.gm_show_error (
     807                                  error = _(
     808                                          'Passphrases do not match.\n'
     809                                          '\n'
     810                                          'Retry, or abort with an empty passphrase.'
     811                                  ),
     812                                  title = msg_title
     813                          )
     814                  return zip_pwd
     815
     816          #--------------------------------------------------------
     817          # file drop target API
     818          #--------------------------------------------------------
     819 -        def _drop_target_consume_filenames(self, filenames):
     820                  pat = gmPerson.gmCurrentPatient()
     821                  if not pat.connected:
     822                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot accept new documents. No active patient.'))
     823                          return
     824
     825                  # dive into folders dropped onto us and extract files (one level deep only)
     826                  real_filenames = []
     827                  for pathname in filenames:
     828                          try:
     829                                  files = os.listdir(pathname)
     830                                  gmDispatcher.send(signal='statustext', msg=_('Extracting files from folder [%s] ...') % pathname)
     831                                  for file in files:
     832                                          fullname = os.path.join(pathname, file)
     833                                          if not os.path.isfile(fullname):
     834                                                  continue
     835                                          real_filenames.append(fullname)
     836                          except OSError:
     837                                  real_filenames.append(pathname)
     838
     839                  if not pat.export_area.add_files(real_filenames, hint = _('Drag&Drop')):
     840                          gmGuiHelpers.gm_show_error (
     841                                  title = _('Adding files to export area'),
     842                                  error = _('Cannot add (some of) the following files to the export area:\n%s ') % '\n '.join(real_filenames)
     843                          )
     844          #--------------------------------------------------------
     845          # reget mixin API
     846          #
     847          # remember to call
     848          #       self._schedule_data_reget()
     849          # whenever you learn of data changes from database
     850          # listener threads, dispatcher signals etc.
     851          #--------------------------------------------------------
     852 -        def _populate_with_data(self):
     853                  pat = gmPerson.gmCurrentPatient()
     854                  if not pat.connected:
     855                          return True
     856
     857                  items = pat.export_area.items
     858                  self._LCTRL_items.set_string_items ([
     859                          [       i['created_by'],
     860                                  gmDateTime.pydt_strftime(i['created_when'], '%Y %b %d %H:%M'),
     861                                  i['description']
     862                          ] for i in items
     863                  ])
     864                  self._LCTRL_items.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
     865                  self._LCTRL_items.set_data(items)
     866
     867                  self._LCTRL_items.SetFocus()
     868
     869                  return True
     870
     871  #============================================================
     872  from Gnumed.wxGladeWidgets import wxgPrintMgrPluginPnl
     873
     874 -class cPrintMgrPluginPnl(wxgPrintMgrPluginPnl.wxgPrintMgrPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
     875          """Panel holding print jobs.
     876
     877          Used as notebook page."""
     878
     879 -        def __init__(self, *args, **kwargs):
     880                  wxgPrintMgrPluginPnl.wxgPrintMgrPluginPnl.__init__(self, *args, **kwargs)
     881                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
     882                  self.__init_ui()
     883                  self.__register_interests()
     884          #--------------------------------------------------------
     885          # event handling
     886          #--------------------------------------------------------
     887 -        def __register_interests(self):
     888                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
     889                  gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
     890                  gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
     891          #--------------------------------------------------------
     892 -        def _on_pre_patient_unselection(self):
     893                  self._RBTN_active_patient_only.Enable(False)
     894                  self._RBTN_all_patients.Value = True
     895                  self._BTN_export_printouts.Enable(False)
     896          #--------------------------------------------------------
     897 -        def _on_post_patient_selection(self):
     898                  self._RBTN_active_patient_only.Enable(True)
     899                  self._BTN_export_printouts.Enable(True)
     900          #--------------------------------------------------------
     901 -        def _on_table_mod(self, *args, **kwargs):
     902                  if kwargs['table'] != 'clin.export_item':
     903                          return
     904                  if self._RBTN_all_patients.Value is True:
     905                          self._schedule_data_reget()
     906                          return
     907                  pat = gmPerson.gmCurrentPatient()
     908                  if not pat.connected:
     909                          return
     910                  if kwargs['pk_identity'] != pat.ID:
     911                          return
     912                  self._schedule_data_reget()
     913          #--------------------------------------------------------
     914 -        def _on_all_patients_selected(self, event):
     915                  event.Skip()
     916                  self._schedule_data_reget()
     917          #--------------------------------------------------------
     918 -        def _on_active_patient_only_selected(self, event):
     919                  event.Skip()
     920                  self._schedule_data_reget()
     921          #--------------------------------------------------------
     922 -        def _on_view_button_pressed(self, event):
     923                  event.Skip()
     924                  printout = self._LCTRL_printouts.get_selected_item_data(only_one = True)
     925                  if printout is None:
     926                          return
     927                  printout.display_via_mime(block = False)
     928          #--------------------------------------------------------
     929 -        def _on_print_button_pressed(self, event):
     930                  event.Skip()
     931                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
     932                  if len(printouts) == 0:
     933                          return
     934
     935                  files2print = []
     936                  for printout in printouts:
     937                          files2print.append(printout.save_to_file())
     938
     939                  if len(files2print) == 0:
     940                          return
     941
     942                  jobtype = 'print_manager'
     943                  printed = gmPrinting.print_files(filenames = files2print, jobtype = jobtype, verbose = _cfg.get(option = 'debug'))
     944                  if not printed:
     945                          gmGuiHelpers.gm_show_error (
     946                                  aMessage = _('Error printing documents.'),
     947                                  aTitle = _('Printing [%s]') % jobtype
     948                          )
     949                          return False
     950
     951                  return True
     952          #--------------------------------------------------------
     953 -        def _on_export_button_pressed(self, event):
     954                  event.Skip()
     955                  pat = gmPerson.gmCurrentPatient()
     956                  if not pat.connected:
     957                          return
     958                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
     959                  for printout in printouts:
     960                          printout.is_print_job = False
     961          #--------------------------------------------------------
     962 -        def _on_delete_button_pressed(self, event):
     963                  event.Skip()
     964                  printouts = self._LCTRL_printouts.get_selected_item_data(only_one = False)
     965                  if len(printouts) == 0:
     966                          return
     967                  if len(printouts) > 1:
     968                          really_delete = gmGuiHelpers.gm_show_question (
     969                                  title = _('Deleting document from export area.'),
     970                                  question = _('Really remove %s selected document(s)\nfrom the patient export area ?') % len(printouts)
     971                          )
     972                          if not really_delete:
     973                                  return
     974                  for printout in printouts:
     975                          gmExportArea.delete_export_item(pk_export_item = printout['pk_export_item'])
     976          #--------------------------------------------------------
     977          # internal API
     978          #--------------------------------------------------------
     979 -        def __init_ui(self):
     980                  self._BTN_export_printouts.Enable(False)
     981          #--------------------------------------------------------
     982          # reget mixin API
     983          #--------------------------------------------------------
     984 -        def _populate_with_data(self):
     985                  if self._RBTN_all_patients.Value is True:
     986                          columns = [_('Patient'), _('Provider'), _('Description')]
     987                          printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
     988                          items = [[
     989                                  '%s, %s (%s)' % (
     990                                          p['lastnames'],
     991                                          p['firstnames'],
     992                                          p['gender']
     993                                  ),
     994                                  p['created_by'],
     995                                  p['description']
     996                          ] for p in printouts ]
     997                  else:
     998                          pat = gmPerson.gmCurrentPatient()
     999                          if pat.connected:
    1000                                  columns = [_('Provider'), _('Created'), _('Description')]
    1001                                  printouts = pat.export_area.get_printouts(order_by = 'created_when, description')
    1002                                  items = [[
    1003                                          p['created_by'],
    1004                                          gmDateTime.pydt_strftime(p['created_when'], '%Y %b %d %H:%M'),
    1005                                          p['description']
    1006                                  ] for p in printouts ]
    1007                          else:
    1008                                  columns = [_('Patient'), _('Provider'), _('Description')]
    1009                                  printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
    1010                                  items = [[
    1011                                          '%s, %s (%s)' % (
    1012                                                  p['lastnames'],
    1013                                                  p['firstnames'],
    1014                                                  p['gender']
    1015                                          ),
    1016                                          p['created_by'],
    1017                                          p['description']
    1018                                  ] for p in printouts ]
    1019                  self._LCTRL_printouts.set_columns(columns)
    1020                  self._LCTRL_printouts.set_string_items(items)
    1021                  self._LCTRL_printouts.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
    1022                  self._LCTRL_printouts.set_data(printouts)
    1023                  self._LCTRL_printouts.SetFocus()
    1024                  return True
    1025