1 """GNUmed patient export area widgets."""
2
3 __author__ = "Karsten.Hilbert@gmx.net"
4 __license__ = "GPL v2 or later"
5
6
7 import sys
8 import logging
9 import os.path
10 import shutil
11
12
13
14 import wx
15
16
17
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 from Gnumed.wxGladeWidgets import wxgExportAreaExportToMediaDlg
45
295
296
297 from Gnumed.wxGladeWidgets import wxgExportAreaSaveAsDlg
298
300
302 self.__patient = kwargs['patient']
303 del kwargs['patient']
304 self.__item_count = kwargs['item_count']
305 del kwargs['item_count']
306 super().__init__(*args, **kwargs)
307
308 self.__init_ui()
309
310
311
312
314 event.Skip()
315 self.__update_ui_state()
316
317
333
334
341
342
363
364
368
369
370
371
373 msg = ('\n' + _('Number of entries to save: %s') + '\n\n' + _('Patient: %s') + '\n') % (
374 self.__item_count,
375 self.__patient['description_gender']
376 )
377 self._LBL_header.Label = msg
378 self._LBL_directory.Label = os.path.join(gmTools.gmPaths().home_dir, 'gnumed', self.__patient.subdir_name) + os.sep
379 self.__update_ui_state()
380
381
383 subdir_name = self.__patient.subdir_name
384 path = self._LBL_directory.Label.strip().rstrip(os.sep).rstrip('/')
385 if self._CHBOX_use_subdirectory.IsChecked():
386
387 if not path.endswith(subdir_name):
388 path = os.path.join(path, subdir_name)
389 self._LBL_directory.Label = path + os.sep
390 else:
391
392 if path.endswith(subdir_name):
393 path = path[:-len(subdir_name)].rstrip(os.sep).rstrip('/')
394 self._LBL_directory.Label = path + os.sep
395
396 is_empty = gmTools.dir_is_empty(directory = path)
397 if is_empty is True:
398 self._LBL_dir_is_empty.Label = ''
399 self._BTN_open_directory.Disable()
400 self._BTN_clear_directory.Disable()
401 self._BTN_save_files.Enable()
402 self._BTN_save_archive.Enable()
403 elif is_empty is False:
404 self._LBL_dir_is_empty.Label = _('directory contains data')
405 self._BTN_open_directory.Enable()
406 self._BTN_clear_directory.Enable()
407 self._BTN_save_files.Disable()
408 self._BTN_save_archive.Disable()
409 else:
410 self._LBL_dir_is_empty.Label = ''
411 self._BTN_open_directory.Disable()
412 self._BTN_clear_directory.Disable()
413 self._BTN_save_files.Enable()
414 self._BTN_save_archive.Enable()
415
416 self.Layout()
417
418
419 from Gnumed.wxGladeWidgets import wxgExportAreaPluginPnl
420
421 -class cExportAreaPluginPnl(wxgExportAreaPluginPnl.wxgExportAreaPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
422 """Panel holding a number of items for further processing.
423
424 Acts on the current patient.
425
426 Used as notebook page."""
432
433
434
435
437 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
438
439 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
440
441
444
445
447 if kwargs['table'] != 'clin.export_item':
448 return
449 pat = gmPerson.gmCurrentPatient()
450 if not pat.connected:
451 return
452 if kwargs['pk_identity'] != pat.ID:
453 return
454 self._schedule_data_reget()
455
456
459
460
467
468
487
488
507
508
519
520
533
534
546
547
561
562
587
588
597
598
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
733
734
738
739
770
771
824
825
827 self._populate_with_data()
828
829
830
831
833 self._LCTRL_items.set_columns([_('By'), _('When'), _('Description')])
834
835 self._BTN_archive_items.Disable()
836
837
838 self.__mail_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-mail_doc')
839 if not self.__mail_script_exists:
840 self._BTN_mail_items.Disable()
841 tt = self._BTN_mail_items.GetToolTipText() + '\n\n' + _('<gm-mail_doc(.bat) not found>')
842 self._BTN_mail_items.SetToolTip(tt)
843
844 self.__fax_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-fax_doc')
845 if not self.__fax_script_exists:
846 self._BTN_fax_items.Disable()
847 tt = self._BTN_fax_items.GetToolTipText() + '\n\n' + _('<gm-fax_doc(.bat) not found>')
848 self._BTN_fax_items.SetToolTip(tt)
849
850 self.__burn_script_exists, path = gmShellAPI.detect_external_binary(binary = r'gm-burn_doc')
851 if not self.__burn_script_exists:
852 self._BTN_burn_items.Disable()
853 tt = self._BTN_burn_items.GetToolTipText() + '\n\n' + _('<gm-burn_doc(.bat) not found>')
854 self._BTN_burn_items.SetToolTip(tt)
855
856
857 dt = gmGuiHelpers.cFileDropTarget(target = self)
858 self.SetDropTarget(dt)
859 dt = gmGuiHelpers.cFileDropTarget(on_drop_callback = self._drop_target_consume_filenames)
860 self._LCTRL_items.SetDropTarget(dt)
861
862
873
874
875 - def __export_as_files(self, msg_title, base_dir=None, encrypt=False, with_metadata=False, items=None):
876
877 data_pwd = None
878 if encrypt:
879 data_pwd = self.__get_password(msg_title)
880 if data_pwd is None:
881 _log.debug('user aborted by not providing the same password twice')
882 gmDispatcher.send(signal = 'statustext', msg = _('Password not provided twice. Aborting.'))
883 return None
884
885 wx.BeginBusyCursor()
886 try:
887 exp_area = gmPerson.gmCurrentPatient().export_area
888 if with_metadata:
889 export_dir = exp_area.export(base_dir = base_dir, items = items, passphrase = data_pwd)
890 else:
891 export_dir = exp_area.dump_items_to_disk(base_dir = base_dir, items = items, passphrase = data_pwd)
892 finally:
893 wx.EndBusyCursor()
894 if export_dir is None:
895 gmGuiHelpers.gm_show_error (
896 aMessage = _('Error exporting entries.'),
897 aTitle = msg_title
898 )
899 return None
900
901 return export_dir
902
903
905
906 zip_pwd = None
907 if encrypt:
908 zip_pwd = self.__get_password(msg_title)
909 if zip_pwd is None:
910 _log.debug('user aborted by not providing the same password twice')
911 gmDispatcher.send(signal = 'statustext', msg = _('Password not provided twice. Aborting.'))
912 return None
913
914 wx.BeginBusyCursor()
915 zip_file = None
916 try:
917 exp_area = gmPerson.gmCurrentPatient().export_area
918 zip_file = exp_area.export_as_zip(passphrase = zip_pwd, items = items)
919 except Exception:
920 _log.exception('cannot create zip file')
921 wx.EndBusyCursor()
922 if zip_file is None:
923 gmGuiHelpers.gm_show_error (
924 aMessage = _('Error creating zip file.'),
925 aTitle = msg_title
926 )
927 return zip_file
928
929
931 while True:
932 data_pwd = wx.GetPasswordFromUser (
933 message = _(
934 'Enter passphrase to protect the data with.\n'
935 '\n'
936 '(minimum length: 5, trailing blanks will be stripped)'
937 ),
938 caption = msg_title
939 )
940
941 data_pwd = data_pwd.rstrip()
942 if len(data_pwd) > 4:
943 break
944 retry = gmGuiHelpers.gm_show_question (
945 title = msg_title,
946 question = _(
947 'Insufficient passphrase.\n'
948 '\n'
949 '(minimum length: 5, trailing blanks will be stripped)\n'
950 '\n'
951 'Enter another passphrase ?'
952 )
953 )
954 if not retry:
955
956 return None
957
958 gmLog2.add_word2hide(data_pwd)
959
960 while True:
961 data_pwd4comparison = wx.GetPasswordFromUser (
962 message = _(
963 'Once more enter passphrase to protect the data with.\n'
964 '\n'
965 '(this will protect you from typos)\n'
966 '\n'
967 'Abort by leaving empty.'
968 ),
969 caption = msg_title
970 )
971 data_pwd4comparison = data_pwd4comparison.rstrip()
972 if data_pwd4comparison == '':
973
974 return None
975 if data_pwd == data_pwd4comparison:
976 break
977 gmGuiHelpers.gm_show_error (
978 error = _(
979 'Passphrases do not match.\n'
980 '\n'
981 'Retry, or abort with an empty passphrase.'
982 ),
983 title = msg_title
984 )
985 return data_pwd
986
987
1011
1012
1014
1015 _log.debug('gm-burn_doc(.bat) API: "DIRECTORY-TO-BURN-FROM"')
1016
1017 found, burn_cmd = gmShellAPI.detect_external_binary('gm-burn_doc')
1018 if not found:
1019 gmDispatcher.send(signal = 'statustext', msg = _('Cannot burn to disk: Helper not found.'))
1020 return False
1021
1022 args = [burn_cmd, base_dir]
1023 wx.BeginBusyCursor()
1024 try:
1025 success, ret_code, stdout = gmShellAPI.run_process(cmd_line = args, verbose = _cfg.get(option = 'debug'))
1026 finally:
1027 wx.EndBusyCursor()
1028 if success:
1029 return True
1030
1031 gmGuiHelpers.gm_show_error (
1032 aMessage = _('Error burning documents to CD/DVD.'),
1033 aTitle = _('Burning documents')
1034 )
1035 return False
1036
1037
1039
1040 msg = _('Documents saved into:\n\n %s') % base_dir
1041 browse_index = gmGuiHelpers.gm_show_question (
1042 title = _('Browsing patient data excerpt'),
1043 question = msg + '\n\n' + _('Browse saved entries ?'),
1044 cancel_button = False
1045 )
1046 if not browse_index:
1047 return
1048
1049 if os.path.isfile(os.path.join(base_dir, 'index.html')):
1050 gmNetworkTools.open_url_in_browser(url = 'file://%s' % os.path.join(base_dir, 'index.html'))
1051 return
1052
1053 gmMimeLib.call_viewer_on_file(base_dir, block = False)
1054
1055
1056
1057
1059 pat = gmPerson.gmCurrentPatient()
1060 if not pat.connected:
1061 gmDispatcher.send(signal = 'statustext', msg = _('Cannot accept new documents. No active patient.'))
1062 return
1063
1064
1065 real_filenames = []
1066 for pathname in filenames:
1067 try:
1068 files = os.listdir(pathname)
1069 gmDispatcher.send(signal='statustext', msg=_('Extracting files from folder [%s] ...') % pathname)
1070 for file in files:
1071 fullname = os.path.join(pathname, file)
1072 if not os.path.isfile(fullname):
1073 continue
1074 real_filenames.append(fullname)
1075 except OSError:
1076 real_filenames.append(pathname)
1077
1078 if not pat.export_area.add_files(real_filenames, hint = _('Drag&Drop')):
1079 gmGuiHelpers.gm_show_error (
1080 title = _('Adding files to export area'),
1081 error = _('Cannot add (some of) the following files to the export area:\n%s ') % '\n '.join(real_filenames)
1082 )
1083
1084
1085
1086
1087
1088
1089
1090
1109
1110
1111 from Gnumed.wxGladeWidgets import wxgPrintMgrPluginPnl
1112
1113 -class cPrintMgrPluginPnl(wxgPrintMgrPluginPnl.wxgPrintMgrPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
1114 """Panel holding print jobs.
1115
1116 Used as notebook page."""
1117
1123
1124
1125
1127 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1128 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1129 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_table_mod)
1130
1132 self._RBTN_active_patient_only.Enable(False)
1133 self._RBTN_all_patients.Value = True
1134 self._BTN_export_printouts.Enable(False)
1135
1137 self._RBTN_active_patient_only.Enable(True)
1138 self._BTN_export_printouts.Enable(True)
1139
1141 if kwargs['table'] != 'clin.export_item':
1142 return
1143 if self._RBTN_all_patients.Value is True:
1144 self._schedule_data_reget()
1145 return
1146 pat = gmPerson.gmCurrentPatient()
1147 if not pat.connected:
1148 return
1149 if kwargs['pk_identity'] != pat.ID:
1150 return
1151 self._schedule_data_reget()
1152
1154 event.Skip()
1155 self._schedule_data_reget()
1156
1158 event.Skip()
1159 self._schedule_data_reget()
1160
1167
1191
1200
1215
1216
1217
1218
1220 self._BTN_export_printouts.Enable(False)
1221
1222
1223
1225 if self._RBTN_all_patients.Value is True:
1226 columns = [_('Patient'), _('Provider'), _('Description')]
1227 printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
1228 items = [[
1229 '%s, %s (%s)' % (
1230 p['lastnames'],
1231 p['firstnames'],
1232 p['gender']
1233 ),
1234 p['created_by'],
1235 p['description']
1236 ] for p in printouts ]
1237 else:
1238 pat = gmPerson.gmCurrentPatient()
1239 if pat.connected:
1240 columns = [_('Provider'), _('Created'), _('Description')]
1241 printouts = pat.export_area.get_printouts(order_by = 'created_when, description')
1242 items = [[
1243 p['created_by'],
1244 gmDateTime.pydt_strftime(p['created_when'], '%Y %b %d %H:%M'),
1245 p['description']
1246 ] for p in printouts ]
1247 else:
1248 columns = [_('Patient'), _('Provider'), _('Description')]
1249 printouts = gmExportArea.get_print_jobs(order_by = 'pk_identity, description')
1250 items = [[
1251 '%s, %s (%s)' % (
1252 p['lastnames'],
1253 p['firstnames'],
1254 p['gender']
1255 ),
1256 p['created_by'],
1257 p['description']
1258 ] for p in printouts ]
1259 self._LCTRL_printouts.set_columns(columns)
1260 self._LCTRL_printouts.set_string_items(items)
1261 self._LCTRL_printouts.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
1262 self._LCTRL_printouts.set_data(printouts)
1263 self._LCTRL_printouts.SetFocus()
1264 return True
1265