1 """GNUmed provider inbox handling widgets.
2 """
3
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5
6 import sys, logging
7
8
9 import wx
10
11
12 if __name__ == '__main__':
13 sys.path.insert(0, '../../')
14 from Gnumed.pycommon import gmI18N
15 from Gnumed.pycommon import gmExceptions
16 from Gnumed.pycommon import gmPG2
17 from Gnumed.pycommon import gmCfg
18 from Gnumed.pycommon import gmTools
19 from Gnumed.pycommon import gmDispatcher
20 from Gnumed.pycommon import gmMatchProvider
21 from Gnumed.pycommon import gmDateTime
22
23 from Gnumed.business import gmPerson
24 from Gnumed.business import gmStaff
25 from Gnumed.business import gmSurgery
26 from Gnumed.business import gmProviderInbox
27
28 from Gnumed.wxpython import gmGuiHelpers
29 from Gnumed.wxpython import gmListWidgets
30 from Gnumed.wxpython import gmPlugin
31 from Gnumed.wxpython import gmRegetMixin
32 from Gnumed.wxpython import gmPhraseWheel
33 from Gnumed.wxpython import gmEditArea
34 from Gnumed.wxpython import gmAuthWidgets
35 from Gnumed.wxpython import gmPatSearchWidgets
36 from Gnumed.wxpython import gmVaccWidgets
37 from Gnumed.wxpython import gmCfgWidgets
38
39
40 _log = logging.getLogger('gm.ui')
41
42 _indicator = {
43 -1: '',
44 0: '',
45 1: '*!!*'
46 }
47
84
97
98
99
101
102 if parent is None:
103 parent = wx.GetApp().GetTopWindow()
104
105 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('showing audit trail'))
106 if conn is None:
107 return False
108
109
110 def refresh(lctrl):
111 cmd = u'SELECT * FROM audit.v_audit_trail ORDER BY audit_when_ts'
112 rows, idx = gmPG2.run_ro_queries(link_obj = conn, queries = [{'cmd': cmd}], get_col_idx = False)
113 lctrl.set_string_items (
114 [ [
115 r['event_when'],
116 r['event_by'],
117 u'%s %s %s' % (
118 gmTools.coalesce(r['row_version_before'], gmTools.u_diameter),
119 gmTools.u_right_arrow,
120 gmTools.coalesce(r['row_version_after'], gmTools.u_diameter)
121 ),
122 r['event_table'],
123 r['event'],
124 r['pk_audit']
125 ] for r in rows ]
126 )
127
128 gmListWidgets.get_choices_from_list (
129 parent = parent,
130 msg = u'',
131 caption = _('GNUmed database audit log ...'),
132 columns = [ _('When'), _('Who'), _('Revisions'), _('Table'), _('Event'), '#' ],
133 single_selection = True,
134 refresh_callback = refresh
135 )
136
137
138
139
184
185 def edit(workplace=None):
186
187 dbcfg = gmCfg.cCfgSQL()
188
189 if workplace is None:
190 dlg = wx.TextEntryDialog (
191 parent = parent,
192 message = _('Enter a descriptive name for the new workplace:'),
193 caption = _('Configuring GNUmed workplaces ...'),
194 defaultValue = u'',
195 style = wx.OK | wx.CENTRE
196 )
197 dlg.ShowModal()
198 workplace = dlg.GetValue().strip()
199 if workplace == u'':
200 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...'))
201 return False
202 curr_plugins = []
203 else:
204 curr_plugins = gmTools.coalesce(dbcfg.get2 (
205 option = u'horstspace.notebook.plugin_load_order',
206 workplace = workplace,
207 bias = 'workplace'
208 ), []
209 )
210
211 msg = _(
212 'Pick the plugin(s) to be loaded the next time the client is restarted under the workplace:\n'
213 '\n'
214 ' [%s]\n'
215 ) % workplace
216
217 picker = gmListWidgets.cItemPickerDlg (
218 parent,
219 -1,
220 title = _('Configuring workplace plugins ...'),
221 msg = msg
222 )
223 picker.set_columns(['Available plugins'], ['Active plugins'])
224 available_plugins = gmPlugin.get_installed_plugins(plugin_dir = 'gui')
225 picker.set_choices(available_plugins)
226 picker.set_picks(picks = curr_plugins)
227 btn_pressed = picker.ShowModal()
228 if btn_pressed != wx.ID_OK:
229 picker.Destroy()
230 return False
231
232 new_plugins = picker.get_picks()
233 picker.Destroy()
234 if new_plugins == curr_plugins:
235 return True
236
237 if new_plugins is None:
238 return True
239
240 dbcfg.set (
241 option = u'horstspace.notebook.plugin_load_order',
242 value = new_plugins,
243 workplace = workplace
244 )
245
246 return True
247
248 def edit_old(workplace=None):
249
250 available_plugins = gmPlugin.get_installed_plugins(plugin_dir='gui')
251
252 dbcfg = gmCfg.cCfgSQL()
253
254 if workplace is None:
255 dlg = wx.TextEntryDialog (
256 parent = parent,
257 message = _('Enter a descriptive name for the new workplace:'),
258 caption = _('Configuring GNUmed workplaces ...'),
259 defaultValue = u'',
260 style = wx.OK | wx.CENTRE
261 )
262 dlg.ShowModal()
263 workplace = dlg.GetValue().strip()
264 if workplace == u'':
265 gmGuiHelpers.gm_show_error(_('Cannot save a new workplace without a name.'), _('Configuring GNUmed workplaces ...'))
266 return False
267 curr_plugins = []
268 choices = available_plugins
269 else:
270 curr_plugins = gmTools.coalesce(dbcfg.get2 (
271 option = u'horstspace.notebook.plugin_load_order',
272 workplace = workplace,
273 bias = 'workplace'
274 ), []
275 )
276 choices = curr_plugins[:]
277 for p in available_plugins:
278 if p not in choices:
279 choices.append(p)
280
281 sels = range(len(curr_plugins))
282 new_plugins = gmListWidgets.get_choices_from_list (
283 parent = parent,
284 msg = _(
285 '\n'
286 'Select the plugin(s) to be loaded the next time\n'
287 'the client is restarted under the workplace:\n'
288 '\n'
289 ' [%s]'
290 '\n'
291 ) % workplace,
292 caption = _('Configuring GNUmed workplaces ...'),
293 choices = choices,
294 selections = sels,
295 columns = [_('Plugins')],
296 single_selection = False
297 )
298
299 if new_plugins == curr_plugins:
300 return True
301
302 if new_plugins is None:
303 return True
304
305 dbcfg.set (
306 option = u'horstspace.notebook.plugin_load_order',
307 value = new_plugins,
308 workplace = workplace
309 )
310
311 return True
312
313 def clone(workplace=None):
314 if workplace is None:
315 return False
316
317 new_name = wx.GetTextFromUser (
318 message = _('Enter a name for the new workplace !'),
319 caption = _('Cloning workplace'),
320 default_value = u'%s-2' % workplace,
321 parent = parent
322 ).strip()
323
324 if new_name == u'':
325 return False
326
327 dbcfg = gmCfg.cCfgSQL()
328 opt = u'horstspace.notebook.plugin_load_order'
329
330 plugins = dbcfg.get2 (
331 option = opt,
332 workplace = workplace,
333 bias = 'workplace'
334 )
335
336 dbcfg.set (
337 option = opt,
338 value = plugins,
339 workplace = new_name
340 )
341
342
343
344 return True
345
346 def refresh(lctrl):
347 workplaces = gmSurgery.gmCurrentPractice().workplaces
348 curr_workplace = gmSurgery.gmCurrentPractice().active_workplace
349 try:
350 sels = [workplaces.index(curr_workplace)]
351 except ValueError:
352 sels = []
353
354 lctrl.set_string_items(workplaces)
355 lctrl.set_selections(selections = sels)
356
357 gmListWidgets.get_choices_from_list (
358 parent = parent,
359 msg = _(
360 '\nSelect the workplace to configure below.\n'
361 '\n'
362 'The currently active workplace is preselected.\n'
363 ),
364 caption = _('Configuring GNUmed workplaces ...'),
365 columns = [_('Workplace')],
366 single_selection = True,
367 refresh_callback = refresh,
368 edit_callback = edit,
369 new_callback = edit,
370 delete_callback = delete,
371 left_extra_button = (_('Clone'), _('Clone the selected workplace'), clone)
372 )
373
375
377
378 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
379
380 query = u"""
381 SELECT DISTINCT ON (label)
382 pk_type,
383 (l10n_type || ' (' || l10n_category || ')')
384 AS label
385 FROM
386 dem.v_inbox_item_type
387 WHERE
388 l10n_type %(fragment_condition)s
389 OR
390 type %(fragment_condition)s
391 OR
392 l10n_category %(fragment_condition)s
393 OR
394 category %(fragment_condition)s
395 ORDER BY label
396 LIMIT 50"""
397
398 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
399 mp.setThresholds(1, 2, 4)
400 self.matcher = mp
401 self.SetToolTipString(_('Select a message type.'))
402
415
417 wx.CallAfter(__display_clinical_reminders)
418
420 pat = gmPerson.gmCurrentPatient()
421 if not pat.connected:
422 return
423 for msg in pat.due_messages:
424 if msg['expiry_date'] is None:
425 exp = u''
426 else:
427 exp = _(' - expires %s') % gmDateTime.pydt_strftime (
428 msg['expiry_date'],
429 '%Y %b %d',
430 accuracy = gmDateTime.acc_days
431 )
432 txt = _(
433 'Due for %s (since %s%s):\n'
434 '%s'
435 '%s'
436 '\n'
437 'Patient: %s\n'
438 'Reminder by: %s'
439 ) % (
440 gmDateTime.format_interval_medically(msg['interval_due']),
441 gmDateTime.pydt_strftime(msg['due_date'], '%Y %b %d', accuracy = gmDateTime.acc_days),
442 exp,
443 gmTools.coalesce(msg['comment'], u'', u'\n%s\n'),
444 gmTools.coalesce(msg['data'], u'', u'\n%s\n'),
445 pat['description_gender'],
446 msg['modified_by']
447 )
448 gmGuiHelpers.gm_show_warning (
449 aTitle = _('Clinical reminder'),
450 aMessage = txt
451 )
452 return
453
454 gmDispatcher.connect(signal = u'post_patient_selection', receiver = _display_clinical_reminders)
455
456
457 from Gnumed.wxGladeWidgets import wxgInboxMessageEAPnl
458
459 -class cInboxMessageEAPnl(wxgInboxMessageEAPnl.wxgInboxMessageEAPnl, gmEditArea.cGenericEditAreaMixin):
460
480
486
487
488
490 validity = True
491
492 if self._TCTRL_subject.GetValue().strip() == u'':
493 validity = False
494 self.display_ctrl_as_valid(ctrl = self._TCTRL_subject, valid = False)
495 else:
496 self.display_ctrl_as_valid(ctrl = self._TCTRL_subject, valid = True)
497
498 if self._PRW_type.GetValue().strip() == u'':
499 validity = False
500 self._PRW_type.display_as_valid(False)
501 else:
502 self._PRW_type.display_as_valid(True)
503
504 missing_receiver = (
505 (self._CHBOX_send_to_me.IsChecked() is False)
506 and
507 (self._PRW_receiver.GetData() is None)
508 )
509
510 missing_patient = (
511 (self._CHBOX_active_patient.IsChecked() is False)
512 and
513 (self._PRW_patient.person is None)
514 )
515
516 if missing_receiver and missing_patient:
517 validity = False
518 self.display_ctrl_as_valid(ctrl = self._CHBOX_send_to_me, valid = False)
519 self._PRW_receiver.display_as_valid(False)
520 self.display_ctrl_as_valid(ctrl = self._CHBOX_active_patient, valid = False)
521 self.display_ctrl_as_valid(ctrl = self._PRW_patient, valid = False)
522 else:
523 self.display_ctrl_as_valid(ctrl = self._CHBOX_send_to_me, valid = True)
524 self._PRW_receiver.display_as_valid(True)
525 self.display_ctrl_as_valid(ctrl = self._CHBOX_active_patient, valid = True)
526 self.display_ctrl_as_valid(ctrl = self._PRW_patient, valid = True)
527
528 return validity
529
531
532 pat_id = None
533 if self._CHBOX_active_patient.GetValue() is True:
534 pat_id = gmPerson.gmCurrentPatient().ID
535 else:
536 if self._PRW_patient.person is not None:
537 pat_id = self._PRW_patient.person.ID
538
539 receiver = None
540 if self._CHBOX_send_to_me.IsChecked():
541 receiver = gmStaff.gmCurrentProvider()['pk_staff']
542 else:
543 if self._PRW_receiver.GetData() is not None:
544 receiver = self._PRW_receiver.GetData()
545
546 msg = gmProviderInbox.create_inbox_message (
547 patient = pat_id,
548 staff = receiver,
549 message_type = self._PRW_type.GetData(can_create = True),
550 subject = self._TCTRL_subject.GetValue().strip()
551 )
552
553 msg['data'] = self._TCTRL_message.GetValue().strip()
554
555 if self._PRW_due.is_valid_timestamp():
556 msg['due_date'] = self._PRW_due.date
557
558 if self._PRW_expiry.is_valid_timestamp():
559 msg['expiry_date'] = self._PRW_expiry.date
560
561 if self._RBTN_normal.GetValue() is True:
562 msg['importance'] = 0
563 elif self._RBTN_high.GetValue() is True:
564 msg['importance'] = 1
565 else:
566 msg['importance'] = -1
567
568 msg.save()
569 self.data = msg
570 return True
571
607
633
635 self._refresh_as_new()
636
690
691
692
694 if self._CHBOX_active_patient.IsChecked():
695 self._PRW_patient.Enable(False)
696 self._PRW_patient.person = None
697 else:
698 self._PRW_patient.Enable(True)
699
701 if self._CHBOX_send_to_me.IsChecked():
702 self._PRW_receiver.Enable(False)
703 self._PRW_receiver.SetData(data = gmStaff.gmCurrentProvider()['pk_staff'])
704 else:
705 self._PRW_receiver.Enable(True)
706 self._PRW_receiver.SetText(value = u'', data = None)
707
723
724 from Gnumed.wxGladeWidgets import wxgProviderInboxPnl
725
726 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
727
728 _item_handlers = {}
729
730
746
747
748
750 self.__populate_inbox()
751 return True
752
753
754
756 gmDispatcher.connect(signal = u'message_inbox_generic_mod_db', receiver = self._on_message_inbox_mod_db)
757 gmDispatcher.connect(signal = u'message_inbox_mod_db', receiver = self._on_message_inbox_mod_db)
758
759 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._on_message_inbox_mod_db)
760 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_message_inbox_mod_db)
761 gmDispatcher.connect(signal = u'doc_mod_db', receiver = self._on_message_inbox_mod_db)
762 gmDispatcher.connect(signal = u'doc_obj_review_mod_db', receiver = self._on_message_inbox_mod_db)
763 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
764
782
806
807
808
810 wx.CallAfter(self._schedule_data_reget)
811 wx.CallAfter(self._RBTN_active_patient.Enable)
812
814 wx.CallAfter(self._schedule_data_reget)
815 gmDispatcher.send(signal = u'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
816
818 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
819 if msg is None:
820 return
821
822 handler_key = '%s.%s' % (msg['category'], msg['type'])
823 try:
824 handle_item = cProviderInboxPnl._item_handlers[handler_key]
825 except KeyError:
826 if msg['pk_patient'] is None:
827 gmGuiHelpers.gm_show_warning (
828 _('No double-click action pre-programmed into\n'
829 'GNUmed for message category and type:\n'
830 '\n'
831 ' [%s]\n'
832 ) % handler_key,
833 _('handling provider inbox item')
834 )
835 return False
836 handle_item = self._goto_patient
837
838 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']):
839 _log.error('item handler returned <False>')
840 _log.error('handler key: [%s]', handler_key)
841 _log.error('message: %s', str(msg))
842 return False
843
844 return True
845
848
850 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
851 if msg is None:
852 return
853
854 if msg['data'] is None:
855 tmp = _('Message: %s') % msg['comment']
856 else:
857 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data'])
858
859 self._TXT_inbox_item_comment.SetValue(tmp)
860
862 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
863 if tmp is None:
864 return
865 self.__focussed_msg = tmp
866
867
868 menu = wx.Menu(title = _('Inbox Message Actions:'))
869
870 if self.__focussed_msg['pk_patient'] is not None:
871 ID = wx.NewId()
872 menu.AppendItem(wx.MenuItem(menu, ID, _('Activate patient')))
873 wx.EVT_MENU(menu, ID, self._on_goto_patient)
874
875 if not self.__focussed_msg['is_virtual']:
876
877 ID = wx.NewId()
878 menu.AppendItem(wx.MenuItem(menu, ID, _('Delete')))
879 wx.EVT_MENU(menu, ID, self._on_delete_focussed_msg)
880
881 ID = wx.NewId()
882 menu.AppendItem(wx.MenuItem(menu, ID, _('Edit')))
883 wx.EVT_MENU(menu, ID, self._on_edit_focussed_msg)
884
885
886
887
888
889
890
891
892 self.PopupMenu(menu, wx.DefaultPosition)
893 menu.Destroy()
894
899
904
907
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
949 return self._goto_patient(pk_patient = self.__focussed_msg['pk_patient'])
950
952 if self.__focussed_msg['is_virtual']:
953 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True)
954 return False
955
956 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_inbox_message']):
957 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.'))
958 return False
959 return True
960
962 if self.__focussed_msg['is_virtual']:
963 gmDispatcher.send(signal = 'statustext', msg = _('This message cannot be edited because it is virtual.'))
964 return False
965 edit_inbox_message(parent = self, message = self.__focussed_msg, single_entry = True)
966 return True
967
969 if self.__focussed_msg['pk_staff'] is None:
970 gmDispatcher.send(signal = 'statustext', msg = _('This message is already visible to all providers.'))
971 return False
972 print "now distributing"
973 return True
974
976
977 wx.BeginBusyCursor()
978
979 msg = _('There is a message about patient [%s].\n\n'
980 'However, I cannot find that\n'
981 'patient in the GNUmed database.'
982 ) % pk_patient
983
984 try:
985 pat = gmPerson.cIdentity(aPK_obj = pk_patient)
986 except gmExceptions.ConstructorError:
987 wx.EndBusyCursor()
988 _log.exception('patient [%s] not found', pk_patient)
989 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
990 return False
991 except:
992 wx.EndBusyCursor()
993 raise
994
995 success = gmPatSearchWidgets.set_active_patient(patient = pat)
996
997 wx.EndBusyCursor()
998
999 if not success:
1000 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
1001 return False
1002
1003 return True
1004
1032
1034
1035 msg = _('Supposedly there are unreviewed results\n'
1036 'for patient [%s]. However, I cannot find\n'
1037 'that patient in the GNUmed database.'
1038 ) % pk_patient
1039
1040 wx.BeginBusyCursor()
1041
1042 try:
1043 pat = gmPerson.cIdentity(aPK_obj = pk_patient)
1044 except gmExceptions.ConstructorError:
1045 wx.EndBusyCursor()
1046 _log.exception('patient [%s] not found', pk_patient)
1047 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
1048 return False
1049
1050 success = gmPatSearchWidgets.set_active_patient(patient = pat)
1051
1052 wx.EndBusyCursor()
1053
1054 if not success:
1055 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
1056 return False
1057
1058 wx.CallAfter(gmDispatcher.send, signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
1059 return True
1060
1089
1090 if __name__ == '__main__':
1091
1092 if len(sys.argv) < 2:
1093 sys.exit()
1094
1095 if sys.argv[1] != 'test':
1096 sys.exit()
1097
1098 gmI18N.activate_locale()
1099 gmI18N.install_domain(domain = 'gnumed')
1100
1104
1106 app = wx.PyWidgetTester(size = (800, 600))
1107 app.SetWidget(cProviderInboxPnl, -1)
1108 app.MainLoop()
1109
1111 app = wx.PyWidgetTester(size = (800, 600))
1112 app.SetWidget(cInboxMessageEAPnl, -1)
1113 app.MainLoop()
1114
1115
1116
1117
1118 test_msg_ea()
1119
1120
1121