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
189
190 pat_id = None
191 if self._CHBOX_active_patient.GetValue() is True:
192 pat_id = gmPerson.gmCurrentPatient().ID
193 else:
194 if self._PRW_patient.person is not None:
195 pat_id = self._PRW_patient.person.ID
196
197 receiver = None
198 if self._CHBOX_send_to_me.IsChecked():
199 receiver = gmStaff.gmCurrentProvider()['pk_staff']
200 else:
201 if self._PRW_receiver.GetData() is not None:
202 receiver = self._PRW_receiver.GetData()
203
204 msg = gmProviderInbox.create_inbox_message (
205 patient = pat_id,
206 staff = receiver,
207 message_type = self._PRW_type.GetData(can_create = True),
208 subject = self._TCTRL_subject.GetValue().strip()
209 )
210
211 msg['data'] = self._TCTRL_message.GetValue().strip()
212
213 if self._PRW_due.is_valid_timestamp():
214 msg['due_date'] = self._PRW_due.date
215
216 if self._PRW_expiry.is_valid_timestamp():
217 msg['expiry_date'] = self._PRW_expiry.date
218
219 if self._RBTN_normal.GetValue() is True:
220 msg['importance'] = 0
221 elif self._RBTN_high.GetValue() is True:
222 msg['importance'] = 1
223 else:
224 msg['importance'] = -1
225
226 msg.save()
227 self.data = msg
228 return True
229
265
291
293 self._refresh_as_new()
294
348
349
350
352 if self._CHBOX_active_patient.IsChecked():
353 self._PRW_patient.Enable(False)
354 self._PRW_patient.person = None
355 else:
356 self._PRW_patient.Enable(True)
357
365
366
382
383
402
403 return gmListWidgets.get_choices_from_list (
404 parent = parent,
405 msg = None,
406 caption = _('Reminders for the current patient'),
407 columns = [ _('Status'), _('Subject'), '#' ],
408 single_selection = False,
409 can_return_empty = True,
410 ignore_OK_button = False,
411 refresh_callback = refresh
412
413
414
415
416
417
418 )
419
420
421 from Gnumed.wxGladeWidgets import wxgProviderInboxPnl
422
423 -class cProviderInboxPnl(wxgProviderInboxPnl.wxgProviderInboxPnl, gmRegetMixin.cRegetOnPaintMixin):
424
425 _item_handlers = {}
426
427
442
443
444
448
450 _log.debug('_populate_with_data() (after _schedule_data_reget ?)')
451 self.__populate_inbox()
452 return True
453
454
455
457 _log.debug('called by notebook plugin API, skipping inbox loading')
458
459 return True
460
462 self._CHBOX_active_patient.SetValue(True)
463 self._TXT_inbox_item_comment.SetValue('')
464 self.__populate_inbox()
465
466
467
469 gmDispatcher.connect(signal = 'dem.message_inbox_mod_db', receiver = self._on_message_inbox_mod_db)
470
471 gmDispatcher.connect(signal = 'clin.reviewed_test_results_mod_db', receiver = self._on_results_mod_db)
472 gmDispatcher.connect(signal = 'dem.identity_mod_db', receiver = self._on_identity_mod_db)
473 gmDispatcher.connect(signal = 'blobs.doc_med_mod_db', receiver = self._on_doc_mod_db)
474 gmDispatcher.connect(signal = 'blobs.reviewed_doc_objs_mod_db', receiver = self._on_doc_obj_review_mod_db)
475 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
476
477
490
491
502
503
505 _log.debug('populating provider inbox')
506
507
508 pk_patient = None
509 if self._CHBOX_active_patient.IsChecked():
510 _log.debug('restricting to active patient')
511 curr_pat = gmPerson.gmCurrentPatient()
512 if curr_pat.connected:
513 pk_patient = curr_pat.ID
514
515 include_without_provider = True
516 if self._CHBOX_active_provider.IsChecked():
517 _log.debug('restricting to active provider directly')
518 include_without_provider = False
519
520
521 if self._RBTN_relevant_messages.GetValue():
522 _log.debug('loading relevant messages')
523 self.__msgs = self.provider.inbox.get_relevant_messages (
524 pk_patient = pk_patient,
525 include_without_provider = include_without_provider
526 )
527 elif self._RBTN_all_messages.GetValue():
528 _log.debug('loading all but expired messages')
529 self.__msgs = self.provider.inbox.get_messages (
530 pk_patient = pk_patient,
531 include_without_provider = include_without_provider,
532 exclude_expired = True,
533 expired_only = False,
534 overdue_only = False,
535 unscheduled_only = False,
536 exclude_unscheduled = False
537 )
538 elif self._RBTN_overdue_messages.GetValue():
539 _log.debug('loading overdue messages only')
540 self.__msgs = self.provider.inbox.get_messages (
541 pk_patient = pk_patient,
542 include_without_provider = include_without_provider,
543 exclude_expired = True,
544 expired_only = False,
545 overdue_only = True,
546 unscheduled_only = False,
547 exclude_unscheduled = True,
548 order_by = 'due_date, importance DESC, received_when DESC'
549 )
550 elif self._RBTN_scheduled_messages.GetValue():
551 _log.debug('loading scheduled messages only')
552 self.__msgs = self.provider.inbox.get_messages (
553 pk_patient = pk_patient,
554 include_without_provider = include_without_provider,
555 exclude_expired = True,
556 expired_only = False,
557 overdue_only = False,
558 unscheduled_only = False,
559 exclude_unscheduled = True,
560 order_by = 'due_date, importance DESC, received_when DESC'
561 )
562 elif self._RBTN_unscheduled_messages.GetValue():
563 _log.debug('loading unscheduled messages only')
564 self.__msgs = self.provider.inbox.get_messages (
565 pk_patient = pk_patient,
566 include_without_provider = include_without_provider,
567 exclude_expired = True,
568 expired_only = False,
569 overdue_only = False,
570 unscheduled_only = True,
571 exclude_unscheduled = False
572 )
573 elif self._RBTN_expired_messages.GetValue():
574 _log.debug('loading expired messages only')
575 self.__msgs = self.provider.inbox.get_messages (
576 pk_patient = pk_patient,
577 include_without_provider = include_without_provider,
578 exclude_expired = False,
579 expired_only = True,
580 overdue_only = False,
581 unscheduled_only = False,
582 exclude_unscheduled = True,
583 order_by = 'expiry_date DESC, importance DESC, received_when DESC'
584 )
585
586 _log.debug('total # of inbox msgs: %s', len(self.__msgs))
587
588 items = []
589 for m in self.__msgs:
590 item = [_indicator[m['importance']]]
591 item.append('%s: %s%s%s' % (
592 gmDateTime.pydt_strftime(m['received_when'], '%Y-%m-%d'),
593 m['modified_by'],
594 gmTools.u_arrow2right,
595 gmTools.coalesce(m['provider'], _('all'))
596 ))
597 if m['due_date'] is None:
598 item.append('')
599 else:
600 if m['is_expired'] is True:
601 item.append(_('expired'))
602 else:
603 if m['is_overdue'] is True:
604 item.append(_('%s overdue') % gmDateTime.format_interval_medically(m['interval_due']))
605 else:
606 item.append(_('due in %s') % gmDateTime.format_interval_medically(m['interval_due']))
607
608 if m['pk_patient'] is None:
609 item.append('')
610 else:
611 item.append('%s, %s%s %s #%s' % (
612 m['lastnames'],
613 m['firstnames'],
614 gmTools.coalesce(m['l10n_gender'], '', ' (%s)'),
615 gmDateTime.pydt_strftime(m['dob'], '%Y %b %d', none_str = ''),
616 m['pk_patient']
617 ))
618 item.append(m['comment'])
619 item.append('%s - %s' % (m['l10n_category'], m['l10n_type']))
620 items.append(item)
621
622 _log.debug('# of list items created from msgs: %s', len(items))
623 self._LCTRL_provider_inbox.set_string_items(items = items)
624 self._LCTRL_provider_inbox.set_data(data = self.__msgs)
625 self._LCTRL_provider_inbox.set_column_widths()
626 self._TXT_inbox_item_comment.SetValue('')
627 self.__update_greeting(len(items))
628
629
630
632 _log.debug('reviewed_test_results_mod_db')
633 self._on_message_inbox_mod_db()
634
636 _log.debug('identity_mod_db')
637 self._on_message_inbox_mod_db()
638
640 _log.debug('doc_obj_review_mod_db')
641 self._on_message_inbox_mod_db()
642
644 _log.debug('doc_mod_db')
645 self._on_message_inbox_mod_db()
646
648 _log.debug('message_inbox_mod_db')
649 self._schedule_data_reget()
650 gmDispatcher.send(signal = 'request_user_attention', msg = _('Please check your GNUmed Inbox !'))
651
653 _log.debug('post_patient_selection')
654 self._CHBOX_active_patient.Enable()
655 self._schedule_data_reget()
656
658
659 try:
660 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
661 except IndexError:
662 _log.exception('problem with provider inbox item data access')
663 gmGuiHelpers.gm_show_error (
664 aTitle = _('handling provider inbox item'),
665 aMessage = _('There was a problem accessing the message data.')
666 )
667 _log.debug('effecting inbox reload')
668 wx.CallAfter(self.__populate_inbox)
669 return False
670
671 if msg is None:
672 return
673
674 handler_key = '%s.%s' % (msg['category'], msg['type'])
675 try:
676 handle_item = cProviderInboxPnl._item_handlers[handler_key]
677 except KeyError:
678 if msg['pk_patient'] is None:
679 gmGuiHelpers.gm_show_warning (
680 _('No double-click action pre-programmed into\n'
681 'GNUmed for message category and type:\n'
682 '\n'
683 ' [%s]\n'
684 ) % handler_key,
685 _('handling provider inbox item')
686 )
687 return False
688 handle_item = self._goto_patient
689
690 if not handle_item(pk_context = msg['pk_context'], pk_patient = msg['pk_patient']):
691 _log.error('item handler returned <False>')
692 _log.error('handler key: [%s]', handler_key)
693 _log.error('message: %s', str(msg))
694 return False
695
696 return True
697
700
702 msg = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
703 if msg is None:
704 return
705
706 if msg['data'] is None:
707 tmp = _('Message: %s') % msg['comment']
708 else:
709 tmp = _('Message: %s\nData: %s') % (msg['comment'], msg['data'])
710
711 self._TXT_inbox_item_comment.SetValue(tmp)
712
714 tmp = self._LCTRL_provider_inbox.get_selected_item_data(only_one = True)
715 if tmp is None:
716 return
717 self.__focussed_msg = tmp
718
719 if self.__focussed_msg['pk_patient'] is not None:
720 menu_item = menu.Append(-1, _('Activate patient'))
721 self.Bind(wx.EVT_MENU, self._on_goto_patient, menu_item)
722
723 if not self.__focussed_msg['is_virtual']:
724 menu_item = menu.Append(-1, _('Delete'))
725 self.Bind(wx.EVT_MENU, self._on_delete_focussed_msg, menu_item)
726 menu_item = menu.Append(-1, _('Edit'))
727 self.Bind(wx.EVT_MENU, self._on_edit_focussed_msg, menu_item)
728
729
730
731
732
733
734
739
741 self._TXT_inbox_item_comment.SetValue('')
742 _log.debug('_on_active_patient_checkbox_ticked')
743 self.__populate_inbox()
744
746 self._TXT_inbox_item_comment.SetValue('')
747 _log.debug('_on_active_provider_checkbox_ticked')
748 self.__populate_inbox()
749
752
755
756
757
759 return self._goto_patient(pk_patient = self.__focussed_msg['pk_patient'])
760
762 if self.__focussed_msg['is_virtual']:
763 gmDispatcher.send(signal = 'statustext', msg = _('You must deal with the reason for this message to remove it from your inbox.'), beep = True)
764 return False
765
766
767 if self.__focussed_msg['pk_staff'] != gmStaff.gmCurrentProvider()['pk_staff']:
768 gmDispatcher.send(signal = 'statustext', msg = _('This message can only be deleted by [%s].') % self.__focussed_msg['provider'], beep = True)
769 return False
770
771 pk_patient = self.__focussed_msg['pk_patient']
772 if pk_patient is not None:
773 emr = gmClinicalRecord.cClinicalRecord(aPKey = pk_patient)
774 epi = emr.add_episode(episode_name = 'administrative', is_open = False)
775 soap_cat = gmTools.bool2subst (
776 (self.__focussed_msg['category'] == 'clinical'),
777 'u',
778 None
779 )
780 narr = _('Deleted inbox message:\n%s') % self.__focussed_msg.format(with_patient = False)
781 emr.add_clin_narrative(note = narr, soap_cat = soap_cat, episode = epi)
782 gmDispatcher.send(signal = 'statustext', msg = _('Recorded deletion of inbox message in EMR.'), beep = False)
783
784 if not self.provider.inbox.delete_message(self.__focussed_msg['pk_inbox_message']):
785 gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.'))
786 return False
787
788 return True
789
791 if self.__focussed_msg['is_virtual']:
792 gmDispatcher.send(signal = 'statustext', msg = _('This message cannot be edited because it is virtual.'))
793 return False
794 edit_inbox_message(parent = self, message = self.__focussed_msg, single_entry = True)
795 return True
796
798 if self.__focussed_msg['pk_staff'] is None:
799 gmDispatcher.send(signal = 'statustext', msg = _('This message is already visible to all providers.'))
800 return False
801 print("now distributing")
802 return True
803
805
806 wx.BeginBusyCursor()
807
808 msg = _('There is a message about patient [%s].\n\n'
809 'However, I cannot find that\n'
810 'patient in the GNUmed database.'
811 ) % pk_patient
812
813 try:
814 pat = gmPerson.cPerson(aPK_obj = pk_patient)
815 except gmExceptions.ConstructorError:
816 wx.EndBusyCursor()
817 _log.exception('patient [%s] not found', pk_patient)
818 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
819 return False
820 except:
821 wx.EndBusyCursor()
822 raise
823
824 success = set_active_patient(patient = pat)
825
826 wx.EndBusyCursor()
827
828 if not success:
829 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
830 return False
831
832 return True
833
835
836 msg = _('Supposedly there are unreviewed documents\n'
837 'for patient [%s]. However, I cannot find\n'
838 'that patient in the GNUmed database.'
839 ) % pk_patient
840
841 wx.BeginBusyCursor()
842
843 try:
844 pat = gmPerson.cPerson(aPK_obj = pk_patient)
845 except gmExceptions.ConstructorError:
846 wx.EndBusyCursor()
847 _log.exception('patient [%s] not found', pk_patient)
848 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
849 return False
850
851 success = set_active_patient(patient = pat)
852
853 wx.EndBusyCursor()
854
855 if not success:
856 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
857 return False
858
859 gmDispatcher.send(signal = 'display_widget', name = 'gmShowMedDocs', sort_mode = 'review')
860 return True
861
863
864 msg = _('Supposedly there are unreviewed results\n'
865 'for patient [%s]. However, I cannot find\n'
866 'that patient in the GNUmed database.'
867 ) % pk_patient
868
869 wx.BeginBusyCursor()
870
871 try:
872 pat = gmPerson.cPerson(aPK_obj = pk_patient)
873 except gmExceptions.ConstructorError:
874 wx.EndBusyCursor()
875 _log.exception('patient [%s] not found', pk_patient)
876 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
877 return False
878
879 success = set_active_patient(patient = pat)
880
881 wx.EndBusyCursor()
882
883 if not success:
884 gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item'))
885 return False
886
887 gmDispatcher.send(signal = 'display_widget', name = 'gmMeasurementsGridPlugin')
888 return True
889
918
919
920 if __name__ == '__main__':
921
922 if len(sys.argv) < 2:
923 sys.exit()
924
925 if sys.argv[1] != 'test':
926 sys.exit()
927
928 gmI18N.activate_locale()
929 gmI18N.install_domain(domain = 'gnumed')
930
932 app = wx.PyWidgetTester(size = (800, 600))
933 app.SetWidget(cProviderInboxPnl, -1)
934 app.MainLoop()
935
937 app = wx.PyWidgetTester(size = (800, 600))
938 app.SetWidget(cInboxMessageEAPnl, -1)
939 app.MainLoop()
940
941
942
943 test_msg_ea()
944
945
946