1 """GNUmed provider inbox handling widgets.
2 """
3
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7 import sys
8 import logging
9
10
11 import wx
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.pycommon import gmI18N
17 from Gnumed.pycommon import gmExceptions
18 from Gnumed.pycommon import gmPG2
19 from Gnumed.pycommon import gmTools
20 from Gnumed.pycommon import gmDispatcher
21 from Gnumed.pycommon import gmMatchProvider
22 from Gnumed.pycommon import gmDateTime
23
24 from Gnumed.business import gmPerson
25 from Gnumed.business import gmStaff
26 from Gnumed.business import gmProviderInbox
27 from Gnumed.business import gmClinicalRecord
28
29 from Gnumed.wxpython import gmGuiHelpers
30 from Gnumed.wxpython import gmListWidgets
31 from Gnumed.wxpython import gmPlugin
32 from Gnumed.wxpython import gmRegetMixin
33 from Gnumed.wxpython import gmPhraseWheel
34 from Gnumed.wxpython import gmEditArea
35 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
36 from Gnumed.wxpython.gmVaccWidgets import manage_vaccinations
37
38
39 _log = logging.getLogger('gm.ui')
40
41 _indicator = {
42 -1: '',
43 0: '',
44 1: '*!!*'
45 }
46
47
49
51
52 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
53
54 query = """
55 SELECT DISTINCT ON (label)
56 pk_type,
57 (l10n_type || ' (' || l10n_category || ')')
58 AS label
59 FROM
60 dem.v_inbox_item_type
61 WHERE
62 l10n_type %(fragment_condition)s
63 OR
64 type %(fragment_condition)s
65 OR
66 l10n_category %(fragment_condition)s
67 OR
68 category %(fragment_condition)s
69 ORDER BY label
70 LIMIT 50"""
71
72 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
73 mp.setThresholds(1, 2, 4)
74 self.matcher = mp
75 self.SetToolTip(_('Select a message type.'))
76
89
90
91 from Gnumed.wxGladeWidgets import wxgInboxMessageEAPnl
92
93 -class cInboxMessageEAPnl(wxgInboxMessageEAPnl.wxgInboxMessageEAPnl, gmEditArea.cGenericEditAreaMixin):
94
114
120
121
122
187
188
190
191 pat_id = None
192 if self._CHBOX_active_patient.GetValue() is True:
193 pat_id = gmPerson.gmCurrentPatient().ID
194 else:
195 if self._PRW_patient.person is not None:
196 pat_id = self._PRW_patient.person.ID
197
198 receiver = None
199 if self._CHBOX_send_to_me.IsChecked():
200 receiver = gmStaff.gmCurrentProvider()['pk_staff']
201 else:
202 if self._PRW_receiver.GetData() is not None:
203 receiver = self._PRW_receiver.GetData()
204
205 msg = gmProviderInbox.create_inbox_message (
206 patient = pat_id,
207 staff = receiver,
208 message_type = self._PRW_type.GetData(can_create = True),
209 subject = self._TCTRL_subject.GetValue().strip()
210 )
211
212 msg['data'] = self._TCTRL_message.GetValue().strip()
213
214 if self._PRW_due.is_valid_timestamp():
215 msg['due_date'] = self._PRW_due.date
216
217 if self._PRW_expiry.is_valid_timestamp():
218 msg['expiry_date'] = self._PRW_expiry.date
219
220 if self._RBTN_normal.GetValue() is True:
221 msg['importance'] = 0
222 elif self._RBTN_high.GetValue() is True:
223 msg['importance'] = 1
224 else:
225 msg['importance'] = -1
226
227 msg.save()
228 self.data = msg
229 return True
230
266
292
294 self._refresh_as_new()
295
349
350
351
353 if self._CHBOX_active_patient.IsChecked():
354 self._PRW_patient.Enable(False)
355 self._PRW_patient.person = None
356 else:
357 self._PRW_patient.Enable(True)
358
366
367
383
384
403
404 return gmListWidgets.get_choices_from_list (
405 parent = parent,
406 msg = None,
407 caption = _('Reminders for the current patient'),
408 columns = [ _('Status'), _('Subject'), '#' ],
409 single_selection = False,
410 can_return_empty = True,
411 ignore_OK_button = False,
412 refresh_callback = refresh
413
414
415
416
417
418
419 )
420
421
422 from Gnumed.wxGladeWidgets import wxgProviderInboxPnl
423
424 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
425
426 _item_handlers = {}
427
428
443
444
445
449
451 _log.debug('_populate_with_data() (after _schedule_data_reget ?)')
452 self.__populate_inbox()
453 return True
454
455
456
458 _log.debug('called by notebook plugin API, skipping inbox loading')
459
460 return True
461
463 self._CHBOX_active_patient.SetValue(True)
464 self._TXT_inbox_item_comment.SetValue('')
465 self.__populate_inbox()
466
467
468
470 gmDispatcher.connect(signal = 'dem.message_inbox_mod_db', receiver = self._on_message_inbox_mod_db)
471
472 gmDispatcher.connect(signal = 'clin.reviewed_test_results_mod_db', receiver = self._on_results_mod_db)
473 gmDispatcher.connect(signal = 'dem.identity_mod_db', receiver = self._on_identity_mod_db)
474 gmDispatcher.connect(signal = 'blobs.doc_med_mod_db', receiver = self._on_doc_mod_db)
475 gmDispatcher.connect(signal = 'blobs.reviewed_doc_objs_mod_db', receiver = self._on_doc_obj_review_mod_db)
476 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
477
478
491
492
503
504
506 _log.debug('populating provider inbox')
507
508
509 pk_patient = None
510 if self._CHBOX_active_patient.IsChecked():
511 _log.debug('restricting to active patient')
512 curr_pat = gmPerson.gmCurrentPatient()
513 if curr_pat.connected:
514 pk_patient = curr_pat.ID
515
516 include_without_provider = True
517 if self._CHBOX_active_provider.IsChecked():
518 _log.debug('restricting to active provider directly')
519 include_without_provider = False
520
521
522 if self._RBTN_relevant_messages.GetValue():
523 _log.debug('loading relevant messages')
524 self.__msgs = self.provider.inbox.get_relevant_messages (
525 pk_patient = pk_patient,
526 include_without_provider = include_without_provider
527 )
528 elif self._RBTN_all_messages.GetValue():
529 _log.debug('loading all but expired messages')
530 self.__msgs = self.provider.inbox.get_messages (
531 pk_patient = pk_patient,
532 include_without_provider = include_without_provider,
533 exclude_expired = True,
534 expired_only = False,
535 overdue_only = False,
536 unscheduled_only = False,
537 exclude_unscheduled = False
538 )
539 elif self._RBTN_overdue_messages.GetValue():
540 _log.debug('loading overdue messages only')
541 self.__msgs = self.provider.inbox.get_messages (
542 pk_patient = pk_patient,
543 include_without_provider = include_without_provider,
544 exclude_expired = True,
545 expired_only = False,
546 overdue_only = True,
547 unscheduled_only = False,
548 exclude_unscheduled = True,
549 order_by = 'due_date, importance DESC, received_when DESC'
550 )
551 elif self._RBTN_scheduled_messages.GetValue():
552 _log.debug('loading scheduled messages only')
553 self.__msgs = self.provider.inbox.get_messages (
554 pk_patient = pk_patient,
555 include_without_provider = include_without_provider,
556 exclude_expired = True,
557 expired_only = False,
558 overdue_only = False,
559 unscheduled_only = False,
560 exclude_unscheduled = True,
561 order_by = 'due_date, importance DESC, received_when DESC'
562 )
563 elif self._RBTN_unscheduled_messages.GetValue():
564 _log.debug('loading unscheduled messages only')
565 self.__msgs = self.provider.inbox.get_messages (
566 pk_patient = pk_patient,
567 include_without_provider = include_without_provider,
568 exclude_expired = True,
569 expired_only = False,
570 overdue_only = False,
571 unscheduled_only = True,
572 exclude_unscheduled = False
573 )
574 elif self._RBTN_expired_messages.GetValue():
575 _log.debug('loading expired messages only')
576 self.__msgs = self.provider.inbox.get_messages (
577 pk_patient = pk_patient,
578 include_without_provider = include_without_provider,
579 exclude_expired = False,
580 expired_only = True,
581 overdue_only = False,
582 unscheduled_only = False,
583 exclude_unscheduled = True,
584 order_by = 'expiry_date DESC, importance DESC, received_when DESC'
585 )
586
587 _log.debug('total # of inbox msgs: %s', len(self.__msgs))
588
589 items = []
590 for m in self.__msgs:
591 item = [_indicator[m['importance']]]
592 item.append('%s: %s%s%s' % (
593 gmDateTime.pydt_strftime(m['received_when'], '%Y-%m-%d'),
594 m['modified_by'],
595 gmTools.u_arrow2right,
596 gmTools.coalesce(m['provider'], _('all'))
597 ))
598 if m['due_date'] is None:
599 item.append('')
600 else:
601 if m['is_expired'] is True:
602 item.append(_('expired'))
603 else:
604 if m['is_overdue'] is True:
605 item.append(_('%s overdue') % gmDateTime.format_interval_medically(m['interval_due']))
606 else:
607 item.append(_('due in %s') % gmDateTime.format_interval_medically(m['interval_due']))
608
609 if m['pk_patient'] is None:
610 item.append('')
611 else:
612 item.append('%s, %s%s %s #%s' % (
613 m['lastnames'],
614 m['firstnames'],
615 gmTools.coalesce(m['l10n_gender'], '', ' (%s)'),
616 gmDateTime.pydt_strftime(m['dob'], '%Y %b %d', none_str = ''),
617 m['pk_patient']
618 ))
619 item.append(m['comment'])
620 item.append('%s - %s' % (m['l10n_category'], m['l10n_type']))
621 items.append(item)
622
623 _log.debug('# of list items created from msgs: %s', len(items))
624 self._LCTRL_provider_inbox.set_string_items(items = items)
625 self._LCTRL_provider_inbox.set_data(data = self.__msgs)
626 self._LCTRL_provider_inbox.set_column_widths()
627 self._TXT_inbox_item_comment.SetValue('')
628 self.__update_greeting(len(items))
629
630
631
633 _log.debug('reviewed_test_results_mod_db')
634 self._on_message_inbox_mod_db()
635
637 _log.debug('identity_mod_db')
638 self._on_message_inbox_mod_db()
639
641 _log.debug('doc_obj_review_mod_db')
642 self._on_message_inbox_mod_db()
643
645 _log.debug('doc_mod_db')
646 self._on_message_inbox_mod_db()
647
649 _log.debug('message_inbox_mod_db')
650 self._schedule_data_reget()
651 gmDispatcher.send(signal = 'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
652
654 _log.debug('post_patient_selection')
655 self._CHBOX_active_patient.Enable()
656 self._schedule_data_reget()
657
659
660 try:
661 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
662 except IndexError:
663 _log.exception('problem with provider inbox item data access')
664 gmGuiHelpers.gm_show_error (
665 aTitle = _('handling provider inbox item'),
666 aMessage = _('There was a problem accessing the message data.')
667 )
668 _log.debug('effecting inbox reload')
669 wx.CallAfter(self.__populate_inbox)
670 return False
671
672 if msg is None:
673 return
674
675 handler_key = '%s.%s' % (msg['category'], msg['type'])
676 try:
677 handle_item = cProviderInboxPnl._item_handlers[handler_key]
678 except KeyError:
679 if msg['pk_patient'] is None:
680 gmGuiHelpers.gm_show_warning (
681 _('No double-click action pre-programmed into\n'
682 'GNUmed for message category and type:\n'
683 '\n'
684 ' [%s]\n'
685 ) % handler_key,
686 _('handling provider inbox item')
687 )
688 return False
689 handle_item = self._goto_patient
690
691 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']):
692 _log.error('item handler returned <False>')
693 _log.error('handler key: [%s]', handler_key)
694 _log.error('message: %s', str(msg))
695 return False
696
697 return True
698
701
703 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
704 if msg is None:
705 return
706
707 if msg['data'] is None:
708 tmp = _('Message: %s') % msg['comment']
709 else:
710 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data'])
711
712 self._TXT_inbox_item_comment.SetValue(tmp)
713
715 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
716 if tmp is None:
717 return
718 self.__focussed_msg = tmp
719
720 if self.__focussed_msg['pk_patient'] is not None:
721 menu_item = menu.Append(-1, _('Activate patient'))
722 self.Bind(wx.EVT_MENU, self._on_goto_patient, menu_item)
723
724 if not self.__focussed_msg['is_virtual']:
725 menu_item = menu.Append(-1, _('Delete'))
726 self.Bind(wx.EVT_MENU, self._on_delete_focussed_msg, menu_item)
727 menu_item = menu.Append(-1, _('Edit'))
728 self.Bind(wx.EVT_MENU, self._on_edit_focussed_msg, menu_item)
729
730
731
732
733
734
735
740
742 self._TXT_inbox_item_comment.SetValue('')
743 _log.debug('_on_active_patient_checkbox_ticked')
744 self.__populate_inbox()
745
747 self._TXT_inbox_item_comment.SetValue('')
748 _log.debug('_on_active_provider_checkbox_ticked')
749 self.__populate_inbox()
750
753
756
757
758
760 return self._goto_patient(pk_patient = self.__focussed_msg['pk_patient'])
761
763 if self.__focussed_msg['is_virtual']:
764 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True)
765 return False
766
767
768 if self.__focussed_msg['pk_staff'] is not None:
769
770 if self.__focussed_msg['pk_staff'] != gmStaff.gmCurrentProvider()['pk_staff']:
771 gmDispatcher.send(signal = 'statustext', msg = _('This message can only be deleted by [%s].') % self.__focussed_msg['provider'], beep = True)
772 return False
773
774 pk_patient = self.__focussed_msg['pk_patient']
775 if pk_patient is not None:
776 emr = gmClinicalRecord.cClinicalRecord(aPKey = pk_patient)
777 epi = emr.add_episode(episode_name = 'administrative', is_open = False)
778 soap_cat = gmTools.bool2subst (
779 (self.__focussed_msg['category'] == 'clinical'),
780 'u',
781 None
782 )
783 narr = _('Deleted inbox message:\n%s') % self.__focussed_msg.format(with_patient = False)
784 emr.add_clin_narrative(note = narr, soap_cat = soap_cat, episode = epi)
785 gmDispatcher.send(signal = 'statustext', msg = _('Recorded deletion of inbox message in EMR.'), beep = False)
786
787 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_inbox_message']):
788 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.'))
789 return False
790
791 return True
792
794 if self.__focussed_msg['is_virtual']:
795 gmDispatcher.send(signal = 'statustext', msg = _('This message cannot be edited because it is virtual.'))
796 return False
797 edit_inbox_message(parent = self, message = self.__focussed_msg, single_entry = True)
798 return True
799
801 if self.__focussed_msg['pk_staff'] is None:
802 gmDispatcher.send(signal = 'statustext', msg = _('This message is already visible to all providers.'))
803 return False
804 print("now distributing")
805 return True
806
808
809 wx.BeginBusyCursor()
810 msg = _('There is a message about patient [%s].\n\n'
811 'However, I cannot find that\n'
812 'patient in the GNUmed database.'
813 ) % pk_patient
814 pat = None
815 try:
816 pat = gmPerson.cPerson(aPK_obj = pk_patient)
817 except gmExceptions.ConstructorError:
818 _log.exception('patient [%s] not found', pk_patient)
819 finally:
820 wx.EndBusyCursor()
821 if pat is None:
822 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
823 return False
824
825 success = set_active_patient(patient = pat)
826 if not success:
827 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
828 return False
829
830 return True
831
833
834 msg = _('Supposedly there are unreviewed documents\n'
835 'for patient [%s]. However, I cannot find\n'
836 'that patient in the GNUmed database.'
837 ) % pk_patient
838
839 wx.BeginBusyCursor()
840
841 try:
842 pat = gmPerson.cPerson(aPK_obj = pk_patient)
843 except gmExceptions.ConstructorError:
844 wx.EndBusyCursor()
845 _log.exception('patient [%s] not found', pk_patient)
846 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
847 return False
848
849 success = set_active_patient(patient = pat)
850
851 wx.EndBusyCursor()
852
853 if not success:
854 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
855 return False
856
857 gmDispatcher.send(signal = 'display_widget', name = 'gmShowMedDocs', sort_mode = 'review')
858 return True
859
861
862 msg = _('Supposedly there are unreviewed results\n'
863 'for patient [%s]. However, I cannot find\n'
864 'that patient in the GNUmed database.'
865 ) % pk_patient
866
867 wx.BeginBusyCursor()
868
869 try:
870 pat = gmPerson.cPerson(aPK_obj = pk_patient)
871 except gmExceptions.ConstructorError:
872 wx.EndBusyCursor()
873 _log.exception('patient [%s] not found', pk_patient)
874 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
875 return False
876
877 success = set_active_patient(patient = pat)
878
879 wx.EndBusyCursor()
880
881 if not success:
882 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
883 return False
884
885 gmDispatcher.send(signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
886 return True
887
889
890 msg = _('Supposedly there are conflicting vaccinations\n'
891 'for patient [%s]. However, I cannot find\n'
892 'that patient in the GNUmed database.'
893 ) % pk_patient
894
895 wx.BeginBusyCursor()
896
897 try:
898 pat = gmPerson.cPerson(aPK_obj = pk_patient)
899 except gmExceptions.ConstructorError:
900 wx.EndBusyCursor()
901 _log.exception('patient [%s] not found', pk_patient)
902 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
903 return False
904
905 success = set_active_patient(patient = pat)
906
907 wx.EndBusyCursor()
908
909 if not success:
910 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
911 return False
912
913 wx.CallAfter(manage_vaccinations)
914
915 return True
916
917
918 if __name__ == '__main__':
919
920 if len(sys.argv) < 2:
921 sys.exit()
922
923 if sys.argv[1] != 'test':
924 sys.exit()
925
926 gmI18N.activate_locale()
927 gmI18N.install_domain(domain = 'gnumed')
928
930 app = wx.PyWidgetTester(size = (800, 600))
931 app.SetWidget(cProviderInboxPnl, -1)
932 app.MainLoop()
933
935 app = wx.PyWidgetTester(size = (800, 600))
936 app.SetWidget(cInboxMessageEAPnl, -1)
937 app.MainLoop()
938
939
940
941 test_msg_ea()
942
943
944