1 """GNUmed EMR structure editors
2
3 This module contains widgets to create and edit EMR structural
4 elements (issues, enconters, episodes).
5
6 This is based on initial work and ideas by Syan <kittylitter@swiftdsl.com.au>
7 and Karsten <Karsten.Hilbert@gmx.net>.
8 """
9
10 __version__ = "$Revision: 1.114 $"
11 __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
12 __license__ = "GPL"
13
14
15 import sys, re, datetime as pydt, logging, time
16
17
18
19 import wx
20
21
22
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
26 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery, gmPersonSearch
27 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
28 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
29 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
30
31
32 _log = logging.getLogger('gm.ui')
33 _log.info(__version__)
34
35
36
47
48 def delete(procedure=None):
49 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
50 return True
51
52 gmDispatcher.send (
53 signal = u'statustext',
54 msg = _('Cannot delete performed procedure.'),
55 beep = True
56 )
57 return False
58
59 def refresh(lctrl):
60 procs = emr.get_performed_procedures()
61
62 items = [
63 [
64 u'%s%s' % (
65 p['clin_when'].strftime('%Y-%m-%d'),
66 gmTools.bool2subst (
67 p['is_ongoing'],
68 _(' (ongoing)'),
69 gmTools.coalesce (
70 initial = p['clin_end'],
71 instead = u'',
72 template_initial = u' - %s',
73 function_initial = ('strftime', u'%Y-%m-%d')
74 )
75 )
76 ),
77 p['clin_where'],
78 p['episode'],
79 p['performed_procedure']
80 ] for p in procs
81 ]
82 lctrl.set_string_items(items = items)
83 lctrl.set_data(data = procs)
84
85 gmListWidgets.get_choices_from_list (
86 parent = parent,
87 msg = _('\nSelect the procedure you want to edit !\n'),
88 caption = _('Editing performed procedures ...'),
89 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
90 single_selection = True,
91 edit_callback = edit,
92 new_callback = edit,
93 delete_callback = delete,
94 refresh_callback = refresh
95 )
96
108
109 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
110
111 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
112
121
123 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
124 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
125 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
126 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
127 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
128
129
130 mp = gmMatchProvider.cMatchProvider_SQL2 (
131 queries = [
132 u"""
133 SELECT DISTINCT ON (data) data, location
134 FROM (
135 SELECT
136 clin_where as data,
137 clin_where as location
138 FROM
139 clin.procedure
140 WHERE
141 clin_where %(fragment_condition)s
142
143 UNION ALL
144
145 SELECT
146 narrative as data,
147 narrative as location
148 FROM
149 clin.hospital_stay
150 WHERE
151 narrative %(fragment_condition)s
152 ) as union_result
153 ORDER BY data
154 LIMIT 25"""
155 ]
156 )
157 mp.setThresholds(2, 4, 6)
158 self._PRW_location.matcher = mp
159
160
161 mp = gmMatchProvider.cMatchProvider_SQL2 (
162 queries = [
163 u"""
164 select distinct on (narrative) narrative, narrative
165 from clin.procedure
166 where narrative %(fragment_condition)s
167 order by narrative
168 limit 25
169 """ ]
170 )
171 mp.setThresholds(2, 4, 6)
172 self._PRW_procedure.matcher = mp
173
175 stay = self._PRW_hospital_stay.GetData()
176 if stay is None:
177 self._PRW_hospital_stay.SetText()
178 self._PRW_location.Enable(True)
179 self._PRW_episode.Enable(True)
180 self._LBL_hospital_details.SetLabel(u'')
181 else:
182 self._PRW_location.SetText()
183 self._PRW_location.Enable(False)
184 self._PRW_episode.SetText()
185 self._PRW_episode.Enable(False)
186 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
187
189 if self._PRW_location.GetValue().strip() == u'':
190 self._PRW_hospital_stay.Enable(True)
191
192 else:
193 self._PRW_hospital_stay.SetText()
194 self._PRW_hospital_stay.Enable(False)
195 self._PRW_hospital_stay.display_as_valid(True)
196
197
209
232
233
234
292
327
329 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
330
331 if self._DPRW_end.GetData() is None:
332 self.data['clin_end'] = None
333 else:
334 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
335
336 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
337
338 if self._PRW_hospital_stay.GetData() is None:
339 self.data['pk_hospital_stay'] = None
340 self.data['clin_where'] = self._PRW_location.GetValue().strip()
341 self.data['pk_episode'] = self._PRW_episode.GetData()
342 else:
343 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
344 self.data['clin_where'] = None
345 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
346 self.data['pk_episode'] = stay['pk_episode']
347
348 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
349
350 self.data.save()
351 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
352
353 return True
354
356 self._DPRW_date.SetText()
357 self._DPRW_end.SetText()
358 self._CHBOX_ongoing.SetValue(False)
359 self._CHBOX_ongoing.Enable(True)
360 self._PRW_hospital_stay.SetText()
361 self._PRW_location.SetText()
362 self._PRW_episode.SetText()
363 self._PRW_procedure.SetText()
364 self._PRW_codes.SetText()
365
366 self._PRW_procedure.SetFocus()
367
398
410
411
412
417
433
434
435
446
447 def delete(stay=None):
448 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
449 return True
450 gmDispatcher.send (
451 signal = u'statustext',
452 msg = _('Cannot delete hospitalization.'),
453 beep = True
454 )
455 return False
456
457 def refresh(lctrl):
458 stays = emr.get_hospital_stays()
459 items = [
460 [
461 s['admission'].strftime('%Y-%m-%d'),
462 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
463 s['episode'],
464 gmTools.coalesce(s['hospital'], u'')
465 ] for s in stays
466 ]
467 lctrl.set_string_items(items = items)
468 lctrl.set_data(data = stays)
469
470 gmListWidgets.get_choices_from_list (
471 parent = parent,
472 msg = _("The patient's hospitalizations:\n"),
473 caption = _('Editing hospitalizations ...'),
474 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
475 single_selection = True,
476 edit_callback = edit,
477 new_callback = edit,
478 delete_callback = delete,
479 refresh_callback = refresh
480 )
481
482
494
496 """Phrasewheel to allow selection of a hospitalization."""
498
499 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
500
501 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
502
503 mp = gmMatchProvider.cMatchProvider_SQL2 (
504 queries = [
505 u"""
506 select
507 pk_hospital_stay,
508 descr
509 from (
510 select distinct on (pk_hospital_stay)
511 pk_hospital_stay,
512 descr
513 from
514 (select
515 pk_hospital_stay,
516 (
517 to_char(admission, 'YYYY-Mon-DD')
518 || coalesce((' (' || hospital || '):'), ': ')
519 || episode
520 || coalesce((' (' || health_issue || ')'), '')
521 ) as descr
522 from
523 clin.v_pat_hospital_stays
524 where
525 %(ctxt_pat)s
526
527 hospital %(fragment_condition)s
528 or
529 episode %(fragment_condition)s
530 or
531 health_issue %(fragment_condition)s
532 ) as the_stays
533 ) as distinct_stays
534 order by descr
535 limit 25
536 """ ],
537 context = ctxt
538 )
539 mp.setThresholds(3, 4, 6)
540 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
541
542 self.matcher = mp
543 self.selection_only = True
544
545 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
546
547 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
548
552
553
554
556
557 valid = True
558
559 if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
560 valid = False
561 gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospitalization.'), beep = True)
562
563 if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
564 if self._PRW_discharge.date is not None:
565 if not self._PRW_discharge.date > self._PRW_admission.date:
566 valid = False
567 self._PRW_discharge.display_as_valid(False)
568 gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospitalization.'), beep = True)
569
570 if self._PRW_episode.GetValue().strip() == u'':
571 valid = False
572 self._PRW_episode.display_as_valid(False)
573 gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospitalization.'), beep = True)
574
575 return (valid is True)
576
589
599
605
615
617 print "this was not expected to be used in this edit area"
618
619
620
629
630 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
631
633 if parent is None:
634 parent = wx.GetApp().GetTopWindow()
635
636
637 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
638 if dlg.ShowModal() == wx.ID_OK:
639 dlg.Destroy()
640 return True
641 dlg.Destroy()
642 return False
643
646
647 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
648
649 if patient is None:
650 patient = gmPerson.gmCurrentPatient()
651
652 if not patient.connected:
653 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
654 return False
655
656 if parent is None:
657 parent = wx.GetApp().GetTopWindow()
658
659 emr = patient.get_emr()
660
661
662 def refresh(lctrl):
663 if encounters is None:
664 encs = emr.get_encounters()
665 else:
666 encs = encounters
667
668 items = [
669 [
670 e['started'].strftime('%x %H:%M'),
671 e['last_affirmed'].strftime('%H:%M'),
672 e['l10n_type'],
673 gmTools.coalesce(e['reason_for_encounter'], u''),
674 gmTools.coalesce(e['assessment_of_encounter'], u''),
675 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
676 e['pk_encounter']
677 ] for e in encs
678 ]
679 lctrl.set_string_items(items = items)
680 lctrl.set_data(data = encs)
681 active_pk = emr.active_encounter['pk_encounter']
682 for idx in range(len(encs)):
683 e = encs[idx]
684 if e['pk_encounter'] == active_pk:
685 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
686
687 def new():
688 cfg_db = gmCfg.cCfgSQL()
689
690 enc_type = cfg_db.get2 (
691 option = u'encounter.default_type',
692 workplace = gmSurgery.gmCurrentPractice().active_workplace,
693 bias = u'user',
694 default = u'in surgery'
695 )
696 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
697 return edit_encounter(parent = parent, encounter = enc)
698
699 def edit(enc=None):
700 return edit_encounter(parent = parent, encounter = enc)
701
702 def edit_active(enc=None):
703 return edit_encounter(parent = parent, encounter = emr.active_encounter)
704
705 def start_new(enc=None):
706 start_new_encounter(emr = emr)
707 return True
708
709 return gmListWidgets.get_choices_from_list (
710 parent = parent,
711 msg = _("The patient's encounters.\n"),
712 caption = _('Encounters ...'),
713 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
714 can_return_empty = False,
715 single_selection = single_selection,
716 refresh_callback = refresh,
717 edit_callback = edit,
718 new_callback = new,
719 ignore_OK_button = ignore_OK_button,
720 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
721 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
722 )
723
725 """This is used as the callback when the EMR detects that the
726 patient was here rather recently and wants to ask the
727 provider whether to continue the recent encounter.
728 """
729 if parent is None:
730 parent = wx.GetApp().GetTopWindow()
731
732 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
733 parent = None,
734 id = -1,
735 caption = caption,
736 question = msg,
737 button_defs = [
738 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
739 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
740 ],
741 show_checkbox = False
742 )
743
744 result = dlg.ShowModal()
745 dlg.Destroy()
746
747 if result == wx.ID_YES:
748 return True
749
750 return False
751
753
754 if parent is None:
755 parent = wx.GetApp().GetTopWindow()
756
757
758 def edit(enc_type=None):
759 return edit_encounter_type(parent = parent, encounter_type = enc_type)
760
761 def delete(enc_type=None):
762 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
763 return True
764 gmDispatcher.send (
765 signal = u'statustext',
766 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
767 beep = True
768 )
769 return False
770
771 def refresh(lctrl):
772 enc_types = gmEMRStructItems.get_encounter_types()
773 lctrl.set_string_items(items = enc_types)
774
775 gmListWidgets.get_choices_from_list (
776 parent = parent,
777 msg = _('\nSelect the encounter type you want to edit !\n'),
778 caption = _('Managing encounter types ...'),
779 columns = [_('Local name'), _('Encounter type')],
780 single_selection = True,
781 edit_callback = edit,
782 new_callback = edit,
783 delete_callback = delete,
784 refresh_callback = refresh
785 )
786
796
798
800 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
801
802 cmd = u"""
803 SELECT -- DISTINCT ON (data)
804 pk_encounter
805 AS data,
806 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type
807 AS list_label,
808 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
809 AS field_label
810 FROM
811 clin.v_pat_encounters
812 WHERE
813 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s
814 OR
815 l10n_type %(fragment_condition)s
816 OR
817 type %(fragment_condition)s
818 %(ctxt_patient)s
819 ORDER BY
820 list_label
821 LIMIT
822 30
823 """
824 context = {'ctxt_patient': {
825 'where_part': u'AND pk_patient = %(patient)s',
826 'placeholder': u'patient'
827 }}
828
829 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context)
830 self.matcher._SQL_data2match = u"""
831 SELECT
832 pk_encounter
833 AS data,
834 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type
835 AS list_label,
836 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
837 AS field_label
838 FROM
839 clin.v_pat_encounters
840 WHERE
841 pk_encounter = %(pk)s
842 """
843 self.matcher.setThresholds(1, 3, 5)
844 self.selection_only = True
845
846 self.set_context(context = 'patient', val = None)
847
854
865
867 """Phrasewheel to allow selection of encounter type.
868
869 - user input interpreted as encounter type in English or local language
870 - data returned is pk of corresponding encounter type or None
871 """
873
874 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
875
876 mp = gmMatchProvider.cMatchProvider_SQL2 (
877 queries = [
878 u"""
879 SELECT
880 data,
881 field_label,
882 list_label
883 FROM (
884 SELECT DISTINCT ON (data) *
885 FROM (
886 SELECT
887 pk AS data,
888 _(description) AS field_label,
889 case
890 when _(description) = description then _(description)
891 else _(description) || ' (' || description || ')'
892 end AS list_label
893 FROM
894 clin.encounter_type
895 WHERE
896 _(description) %(fragment_condition)s
897 OR
898 description %(fragment_condition)s
899 ) AS q_distinct_pk
900 ) AS q_ordered
901 ORDER BY
902 list_label
903 """ ]
904 )
905 mp.setThresholds(2, 4, 6)
906
907 self.matcher = mp
908 self.selection_only = True
909 self.picklist_delay = 50
910
912
917
918
919
920
921
951
964
974
976 self._TCTRL_l10n_name.SetValue(u'')
977 self._TCTRL_name.SetValue(u'')
978 self._TCTRL_name.Enable(True)
979
981 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
982 self._TCTRL_name.SetValue(self.data['description'])
983
984 self._TCTRL_name.Enable(False)
985
987 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
988 self._TCTRL_name.SetValue(self.data['description'])
989 self._TCTRL_name.Enable(True)
990
991
992
993
994
995
996 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
997
999
1001 try:
1002 self.__encounter = kwargs['encounter']
1003 del kwargs['encounter']
1004 except KeyError:
1005 self.__encounter = None
1006
1007 try:
1008 msg = kwargs['msg']
1009 del kwargs['msg']
1010 except KeyError:
1011 msg = None
1012
1013 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
1014
1015 self.refresh(msg = msg)
1016
1017
1018
1019 - def refresh(self, encounter=None, msg=None):
1020
1021 if msg is not None:
1022 self._LBL_instructions.SetLabel(msg)
1023
1024 if encounter is not None:
1025 self.__encounter = encounter
1026
1027 if self.__encounter is None:
1028 return True
1029
1030
1031
1032 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
1033 self._LBL_patient.SetLabel(pat.get_description_gender())
1034
1035 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
1036
1037 fts = gmDateTime.cFuzzyTimestamp (
1038 timestamp = self.__encounter['started'],
1039 accuracy = gmDateTime.acc_minutes
1040 )
1041 self._PRW_start.SetText(fts.format_accurately(), data=fts)
1042
1043 fts = gmDateTime.cFuzzyTimestamp (
1044 timestamp = self.__encounter['last_affirmed'],
1045 accuracy = gmDateTime.acc_minutes
1046 )
1047 self._PRW_end.SetText(fts.format_accurately(), data=fts)
1048
1049
1050 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
1051 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
1052 self._PRW_rfe_codes.SetText(val, data)
1053
1054
1055 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
1056 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
1057 self._PRW_aoe_codes.SetText(val, data)
1058
1059
1060 if self.__encounter['last_affirmed'] == self.__encounter['started']:
1061 self._PRW_end.SetFocus()
1062 else:
1063 self._TCTRL_aoe.SetFocus()
1064
1065 return True
1066
1068
1069 if self._PRW_encounter_type.GetData() is None:
1070 self._PRW_encounter_type.SetBackgroundColour('pink')
1071 self._PRW_encounter_type.Refresh()
1072 self._PRW_encounter_type.SetFocus()
1073 return False
1074 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1075 self._PRW_encounter_type.Refresh()
1076
1077
1078 if self._PRW_start.GetValue().strip() == u'':
1079 self._PRW_start.SetBackgroundColour('pink')
1080 self._PRW_start.Refresh()
1081 self._PRW_start.SetFocus()
1082 return False
1083 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False):
1084 self._PRW_start.SetBackgroundColour('pink')
1085 self._PRW_start.Refresh()
1086 self._PRW_start.SetFocus()
1087 return False
1088 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1089 self._PRW_start.Refresh()
1090
1091
1092
1093
1094
1095
1096
1097 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False):
1098 self._PRW_end.SetBackgroundColour('pink')
1099 self._PRW_end.Refresh()
1100 self._PRW_end.SetFocus()
1101 return False
1102 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1103 self._PRW_end.Refresh()
1104
1105 return True
1106
1108 if not self.__is_valid_for_save():
1109 return False
1110
1111 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1112 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1113 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1114 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1115 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1116 self.__encounter.save_payload()
1117
1118 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1119 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1120
1121 return True
1122
1123
1125
1127 encounter = kwargs['encounter']
1128 del kwargs['encounter']
1129
1130 try:
1131 button_defs = kwargs['button_defs']
1132 del kwargs['button_defs']
1133 except KeyError:
1134 button_defs = None
1135
1136 try:
1137 msg = kwargs['msg']
1138 del kwargs['msg']
1139 except KeyError:
1140 msg = None
1141
1142 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1143 self.SetSize((450, 280))
1144 self.SetMinSize((450, 280))
1145
1146 if button_defs is not None:
1147 self._BTN_save.SetLabel(button_defs[0][0])
1148 self._BTN_save.SetToolTipString(button_defs[0][1])
1149 self._BTN_close.SetLabel(button_defs[1][0])
1150 self._BTN_close.SetToolTipString(button_defs[1][1])
1151 self.Refresh()
1152
1153 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1154
1155 self.Fit()
1156
1163
1164 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
1165
1167
1172
1174 self._TCTRL_encounter.SetValue(u'')
1175 self._TCTRL_encounter.SetToolTipString(u'')
1176 self._BTN_new.Enable(False)
1177 self._BTN_list.Enable(False)
1178
1180 pat = gmPerson.gmCurrentPatient()
1181 if not pat.connected:
1182 self.clear()
1183 return
1184
1185 enc = pat.get_emr().active_encounter
1186 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
1187 self._TCTRL_encounter.SetToolTipString (
1188 _('The active encounter of the current patient:\n\n%s') %
1189 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
1190 )
1191 self._BTN_new.Enable(True)
1192 self._BTN_list.Enable(True)
1193
1195 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
1196
1197 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
1198
1199
1200 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
1201 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
1202 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1203
1204
1205
1207 wx.CallAfter(self.clear)
1208
1210 wx.CallAfter(self.refresh)
1211 return True
1212
1216
1220
1223
1224
1225
1235
1305
1307 """Prepare changing health issue for an episode.
1308
1309 Checks for two-open-episodes conflict. When this
1310 function succeeds, the pk_health_issue has been set
1311 on the episode instance and the episode should - for
1312 all practical purposes - be ready for save_payload().
1313 """
1314
1315 if not episode['episode_open']:
1316 episode['pk_health_issue'] = target_issue['pk_health_issue']
1317 if save_to_backend:
1318 episode.save_payload()
1319 return True
1320
1321
1322 if target_issue is None:
1323 episode['pk_health_issue'] = None
1324 if save_to_backend:
1325 episode.save_payload()
1326 return True
1327
1328
1329 db_cfg = gmCfg.cCfgSQL()
1330 epi_ttl = int(db_cfg.get2 (
1331 option = u'episode.ttl',
1332 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1333 bias = 'user',
1334 default = 60
1335 ))
1336 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1337 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1338 existing_epi = target_issue.get_open_episode()
1339
1340
1341 if existing_epi is None:
1342 episode['pk_health_issue'] = target_issue['pk_health_issue']
1343 if save_to_backend:
1344 episode.save_payload()
1345 return True
1346
1347
1348 if existing_epi['pk_episode'] == episode['pk_episode']:
1349 episode['pk_health_issue'] = target_issue['pk_health_issue']
1350 if save_to_backend:
1351 episode.save_payload()
1352 return True
1353
1354
1355 move_range = episode.get_access_range()
1356 exist_range = existing_epi.get_access_range()
1357 question = _(
1358 'You want to associate the running episode:\n\n'
1359 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1360 'with the health issue:\n\n'
1361 ' "%(issue_name)s"\n\n'
1362 'There already is another episode running\n'
1363 'for this health issue:\n\n'
1364 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1365 'However, there can only be one running\n'
1366 'episode per health issue.\n\n'
1367 'Which episode do you want to close ?'
1368 ) % {
1369 'new_epi_name': episode['description'],
1370 'new_epi_start': move_range[0].strftime('%m/%y'),
1371 'new_epi_end': move_range[1].strftime('%m/%y'),
1372 'issue_name': target_issue['description'],
1373 'old_epi_name': existing_epi['description'],
1374 'old_epi_start': exist_range[0].strftime('%m/%y'),
1375 'old_epi_end': exist_range[1].strftime('%m/%y')
1376 }
1377 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1378 parent = None,
1379 id = -1,
1380 caption = _('Resolving two-running-episodes conflict'),
1381 question = question,
1382 button_defs = [
1383 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1384 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1385 ]
1386 )
1387 decision = dlg.ShowModal()
1388
1389 if decision == wx.ID_CANCEL:
1390
1391 return False
1392
1393 elif decision == wx.ID_YES:
1394
1395 existing_epi['episode_open'] = False
1396 existing_epi.save_payload()
1397
1398 elif decision == wx.ID_NO:
1399
1400 episode['episode_open'] = False
1401
1402 else:
1403 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1404
1405 episode['pk_health_issue'] = target_issue['pk_health_issue']
1406 if save_to_backend:
1407 episode.save_payload()
1408 return True
1409
1433
1435 """Let user select an episode *description*.
1436
1437 The user can select an episode description from the previously
1438 used descriptions across all episodes across all patients.
1439
1440 Selection is done with a phrasewheel so the user can
1441 type the episode name and matches will be shown. Typing
1442 "*" will show the entire list of episodes.
1443
1444 If the user types a description not existing yet a
1445 new episode description will be returned.
1446 """
1448
1449 mp = gmMatchProvider.cMatchProvider_SQL2 (
1450 queries = [
1451 u"""
1452 SELECT DISTINCT ON (description)
1453 description
1454 AS data,
1455 description
1456 AS field_label,
1457 description || ' ('
1458 || CASE
1459 WHEN is_open IS TRUE THEN _('ongoing')
1460 ELSE _('closed')
1461 END
1462 || ')'
1463 AS list_label
1464 FROM
1465 clin.episode
1466 WHERE
1467 description %(fragment_condition)s
1468 ORDER BY description
1469 LIMIT 30
1470 """
1471 ]
1472 )
1473 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1474 self.matcher = mp
1475
1477 """Let user select an episode.
1478
1479 The user can select an episode from the existing episodes of a
1480 patient. Selection is done with a phrasewheel so the user
1481 can type the episode name and matches will be shown. Typing
1482 "*" will show the entire list of episodes. Closed episodes
1483 will be marked as such. If the user types an episode name not
1484 in the list of existing episodes a new episode can be created
1485 from it if the programmer activated that feature.
1486
1487 If keyword <patient_id> is set to None or left out the control
1488 will listen to patient change signals and therefore act on
1489 gmPerson.gmCurrentPatient() changes.
1490 """
1492
1493 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1494
1495 mp = gmMatchProvider.cMatchProvider_SQL2 (
1496 queries = [
1497 u"""(
1498
1499 select
1500 pk_episode
1501 as data,
1502 description
1503 as field_label,
1504 coalesce (
1505 description || ' - ' || health_issue,
1506 description
1507 ) as list_label,
1508 1 as rank
1509 from
1510 clin.v_pat_episodes
1511 where
1512 episode_open is true and
1513 description %(fragment_condition)s
1514 %(ctxt_pat)s
1515
1516 ) union all (
1517
1518 select
1519 pk_episode
1520 as data,
1521 description
1522 as field_label,
1523 coalesce (
1524 description || _(' (closed)') || ' - ' || health_issue,
1525 description || _(' (closed)')
1526 ) as list_label,
1527 2 as rank
1528 from
1529 clin.v_pat_episodes
1530 where
1531 description %(fragment_condition)s and
1532 episode_open is false
1533 %(ctxt_pat)s
1534
1535 )
1536
1537 order by rank, list_label
1538 limit 30"""
1539 ],
1540 context = ctxt
1541 )
1542
1543 try:
1544 kwargs['patient_id']
1545 except KeyError:
1546 kwargs['patient_id'] = None
1547
1548 if kwargs['patient_id'] is None:
1549 self.use_current_patient = True
1550 self.__register_patient_change_signals()
1551 pat = gmPerson.gmCurrentPatient()
1552 if pat.connected:
1553 mp.set_context('pat', pat.ID)
1554 else:
1555 self.use_current_patient = False
1556 self.__patient_id = int(kwargs['patient_id'])
1557 mp.set_context('pat', self.__patient_id)
1558
1559 del kwargs['patient_id']
1560
1561 gmPhraseWheel.cPhraseWheel.__init__ (
1562 self,
1563 *args,
1564 **kwargs
1565 )
1566 self.matcher = mp
1567
1568
1569
1571 if self.use_current_patient:
1572 return False
1573 self.__patient_id = int(patient_id)
1574 self.set_context('pat', self.__patient_id)
1575 return True
1576
1577 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1580
1582
1583 epi_name = self.GetValue().strip()
1584 if epi_name == u'':
1585 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1586 _log.debug('cannot create episode without name')
1587 return
1588
1589 if self.use_current_patient:
1590 pat = gmPerson.gmCurrentPatient()
1591 else:
1592 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1593
1594 emr = pat.get_emr()
1595 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1596 if epi is None:
1597 self.data = {}
1598 else:
1599 self.SetText (
1600 value = epi_name,
1601 data = epi['pk_episode']
1602 )
1603
1606
1607
1608
1612
1615
1617 if self.use_current_patient:
1618 patient = gmPerson.gmCurrentPatient()
1619 self.set_context('pat', patient.ID)
1620 return True
1621
1622 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1623
1624 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1625
1638
1639
1640
1642
1643 errors = False
1644
1645 if len(self._PRW_description.GetValue().strip()) == 0:
1646 errors = True
1647 self._PRW_description.display_as_valid(False)
1648 self._PRW_description.SetFocus()
1649 else:
1650 self._PRW_description.display_as_valid(True)
1651 self._PRW_description.Refresh()
1652
1653 return not errors
1654
1656
1657 pat = gmPerson.gmCurrentPatient()
1658 emr = pat.get_emr()
1659
1660 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1661 epi['summary'] = self._TCTRL_status.GetValue().strip()
1662 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1663 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1664
1665 issue_name = self._PRW_issue.GetValue().strip()
1666 if len(issue_name) != 0:
1667 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1668 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1669
1670 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1671 gmDispatcher.send (
1672 signal = 'statustext',
1673 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1674 epi['description'],
1675 issue['description']
1676 )
1677 )
1678 gmEMRStructItems.delete_episode(episode = epi)
1679 return False
1680
1681 epi.save()
1682
1683 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1684
1685 self.data = epi
1686 return True
1687
1689
1690 self.data['description'] = self._PRW_description.GetValue().strip()
1691 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1692 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1693 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1694
1695 issue_name = self._PRW_issue.GetValue().strip()
1696 if len(issue_name) == 0:
1697 self.data['pk_health_issue'] = None
1698 else:
1699 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1700 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1701
1702 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1703 gmDispatcher.send (
1704 signal = 'statustext',
1705 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1706 self.data['description'],
1707 issue['description']
1708 )
1709 )
1710 return False
1711
1712 self.data.save()
1713 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1714
1715 return True
1716
1729
1748
1750 self._refresh_as_new()
1751
1752
1753
1763
1765
1766
1767
1769
1770 issues = kwargs['issues']
1771 del kwargs['issues']
1772
1773 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1774
1775 self.SetTitle(_('Select the health issues you are interested in ...'))
1776 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1777
1778 for issue in issues:
1779 if issue['is_confidential']:
1780 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1781 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1782 else:
1783 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1784
1785 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1786 if issue['clinically_relevant']:
1787 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1788 if issue['is_active']:
1789 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1790 if issue['is_cause_of_death']:
1791 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1792
1793 self._LCTRL_items.set_column_widths()
1794 self._LCTRL_items.set_data(data = issues)
1795
1797 """Let the user select a health issue.
1798
1799 The user can select a health issue from the existing issues
1800 of a patient. Selection is done with a phrasewheel so the user
1801 can type the issue name and matches will be shown. Typing
1802 "*" will show the entire list of issues. Inactive issues
1803 will be marked as such. If the user types an issue name not
1804 in the list of existing issues a new issue can be created
1805 from it if the programmer activated that feature.
1806
1807 If keyword <patient_id> is set to None or left out the control
1808 will listen to patient change signals and therefore act on
1809 gmPerson.gmCurrentPatient() changes.
1810 """
1812
1813 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1814
1815 mp = gmMatchProvider.cMatchProvider_SQL2 (
1816
1817 queries = [
1818 u"""
1819 SELECT
1820 data,
1821 field_label,
1822 list_label
1823 FROM ((
1824 SELECT
1825 pk_health_issue AS data,
1826 description AS field_label,
1827 description AS list_label
1828 FROM clin.v_health_issues
1829 WHERE
1830 is_active IS true
1831 AND
1832 description %(fragment_condition)s
1833 AND
1834 %(ctxt_pat)s
1835
1836 ) UNION (
1837
1838 SELECT
1839 pk_health_issue AS data,
1840 description AS field_label,
1841 description || _(' (inactive)') AS list_label
1842 FROM clin.v_health_issues
1843 WHERE
1844 is_active IS false
1845 AND
1846 description %(fragment_condition)s
1847 AND
1848 %(ctxt_pat)s
1849 )) AS union_query
1850 ORDER BY
1851 list_label"""],
1852 context = ctxt
1853 )
1854
1855 try: kwargs['patient_id']
1856 except KeyError: kwargs['patient_id'] = None
1857
1858 if kwargs['patient_id'] is None:
1859 self.use_current_patient = True
1860 self.__register_patient_change_signals()
1861 pat = gmPerson.gmCurrentPatient()
1862 if pat.connected:
1863 mp.set_context('pat', pat.ID)
1864 else:
1865 self.use_current_patient = False
1866 self.__patient_id = int(kwargs['patient_id'])
1867 mp.set_context('pat', self.__patient_id)
1868
1869 del kwargs['patient_id']
1870
1871 gmPhraseWheel.cPhraseWheel.__init__ (
1872 self,
1873 *args,
1874 **kwargs
1875 )
1876 self.matcher = mp
1877
1878
1879
1881 if self.use_current_patient:
1882 return False
1883 self.__patient_id = int(patient_id)
1884 self.set_context('pat', self.__patient_id)
1885 return True
1886
1888 issue_name = self.GetValue().strip()
1889 if issue_name == u'':
1890 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create health issue without name.'), beep = True)
1891 _log.debug('cannot create health issue without name')
1892 return
1893
1894 if self.use_current_patient:
1895 pat = gmPerson.gmCurrentPatient()
1896 else:
1897 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1898
1899 emr = pat.get_emr()
1900 issue = emr.add_health_issue(issue_name = issue_name)
1901
1902 if issue is None:
1903 self.data = {}
1904 else:
1905 self.SetText (
1906 value = issue_name,
1907 data = issue['pk_health_issue']
1908 )
1909
1912
1913
1914
1918
1921
1923 if self.use_current_patient:
1924 patient = gmPerson.gmCurrentPatient()
1925 self.set_context('pat', patient.ID)
1926 return True
1927
1929
1931 try:
1932 msg = kwargs['message']
1933 except KeyError:
1934 msg = None
1935 del kwargs['message']
1936 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1937 if msg is not None:
1938 self._lbl_message.SetLabel(label=msg)
1939
1950
1951 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1952
1953 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1954 """Panel encapsulating health issue edit area functionality."""
1955
1957
1958 try:
1959 issue = kwargs['issue']
1960 except KeyError:
1961 issue = None
1962
1963 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1964
1965 gmEditArea.cGenericEditAreaMixin.__init__(self)
1966
1967
1968 mp = gmMatchProvider.cMatchProvider_SQL2 (
1969 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1970 )
1971 mp.setThresholds(1, 3, 5)
1972 self._PRW_condition.matcher = mp
1973
1974 mp = gmMatchProvider.cMatchProvider_SQL2 (
1975 queries = [u"""
1976 select distinct on (grouping) grouping, grouping from (
1977
1978 select rank, grouping from ((
1979
1980 select
1981 grouping,
1982 1 as rank
1983 from
1984 clin.health_issue
1985 where
1986 grouping %%(fragment_condition)s
1987 and
1988 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1989
1990 ) union (
1991
1992 select
1993 grouping,
1994 2 as rank
1995 from
1996 clin.health_issue
1997 where
1998 grouping %%(fragment_condition)s
1999
2000 )) as union_result
2001
2002 order by rank
2003
2004 ) as order_result
2005
2006 limit 50""" % gmPerson.gmCurrentPatient().ID
2007 ]
2008 )
2009 mp.setThresholds(1, 3, 5)
2010 self._PRW_grouping.matcher = mp
2011
2012 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
2013 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
2014
2015 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
2016 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
2017
2018 self._PRW_year_noted.Enable(True)
2019
2020 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
2021
2022 self.data = issue
2023
2024
2025
2045
2047 pat = gmPerson.gmCurrentPatient()
2048 emr = pat.get_emr()
2049
2050 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
2051
2052 side = u''
2053 if self._ChBOX_left.GetValue():
2054 side += u's'
2055 if self._ChBOX_right.GetValue():
2056 side += u'd'
2057 issue['laterality'] = side
2058
2059 issue['summary'] = self._TCTRL_status.GetValue().strip()
2060 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2061 issue['grouping'] = self._PRW_grouping.GetValue().strip()
2062 issue['is_active'] = self._ChBOX_active.GetValue()
2063 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
2064 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
2065 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
2066
2067 age_noted = self._PRW_age_noted.GetData()
2068 if age_noted is not None:
2069 issue['age_noted'] = age_noted
2070
2071 issue.save()
2072
2073 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2074
2075 self.data = issue
2076 return True
2077
2079
2080 self.data['description'] = self._PRW_condition.GetValue().strip()
2081
2082 side = u''
2083 if self._ChBOX_left.GetValue():
2084 side += u's'
2085 if self._ChBOX_right.GetValue():
2086 side += u'd'
2087 self.data['laterality'] = side
2088
2089 self.data['summary'] = self._TCTRL_status.GetValue().strip()
2090 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2091 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
2092 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
2093 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
2094 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
2095 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
2096
2097 age_noted = self._PRW_age_noted.GetData()
2098 if age_noted is not None:
2099 self.data['age_noted'] = age_noted
2100
2101 self.data.save()
2102 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2103
2104 return True
2105
2107 self._PRW_condition.SetText()
2108 self._ChBOX_left.SetValue(0)
2109 self._ChBOX_right.SetValue(0)
2110 self._PRW_codes.SetText()
2111 self._on_leave_codes()
2112 self._PRW_certainty.SetText()
2113 self._PRW_grouping.SetText()
2114 self._TCTRL_status.SetValue(u'')
2115 self._PRW_age_noted.SetText()
2116 self._PRW_year_noted.SetText()
2117 self._ChBOX_active.SetValue(0)
2118 self._ChBOX_relevant.SetValue(1)
2119 self._ChBOX_confidential.SetValue(0)
2120 self._ChBOX_caused_death.SetValue(0)
2121
2122 return True
2123
2164
2166 return self._refresh_as_new()
2167
2168
2169
2171 if not self._PRW_codes.IsModified():
2172 return True
2173
2174 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2175
2177
2178 if not self._PRW_age_noted.IsModified():
2179 return True
2180
2181 str_age = self._PRW_age_noted.GetValue().strip()
2182
2183 if str_age == u'':
2184 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2185 return True
2186
2187 age = gmDateTime.str2interval(str_interval = str_age)
2188
2189 if age is None:
2190 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
2191 self._PRW_age_noted.SetBackgroundColour('pink')
2192 self._PRW_age_noted.Refresh()
2193 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2194 return True
2195
2196 pat = gmPerson.gmCurrentPatient()
2197 if pat['dob'] is not None:
2198 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2199
2200 if age >= max_age:
2201 gmDispatcher.send (
2202 signal = 'statustext',
2203 msg = _(
2204 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2205 ) % (age, pat.get_medical_age())
2206 )
2207 self._PRW_age_noted.SetBackgroundColour('pink')
2208 self._PRW_age_noted.Refresh()
2209 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2210 return True
2211
2212 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2213 self._PRW_age_noted.Refresh()
2214 self._PRW_age_noted.SetData(data=age)
2215
2216 if pat['dob'] is not None:
2217 fts = gmDateTime.cFuzzyTimestamp (
2218 timestamp = pat['dob'] + age,
2219 accuracy = gmDateTime.acc_months
2220 )
2221 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2222
2223
2224
2225
2226
2227 return True
2228
2230
2231 if not self._PRW_year_noted.IsModified():
2232 return True
2233
2234 year_noted = self._PRW_year_noted.GetData()
2235
2236 if year_noted is None:
2237 if self._PRW_year_noted.GetValue().strip() == u'':
2238 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2239 return True
2240 self._PRW_year_noted.SetBackgroundColour('pink')
2241 self._PRW_year_noted.Refresh()
2242 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2243 return True
2244
2245 year_noted = year_noted.get_pydt()
2246
2247 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2248 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2249 self._PRW_year_noted.SetBackgroundColour('pink')
2250 self._PRW_year_noted.Refresh()
2251 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2252 return True
2253
2254 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2255 self._PRW_year_noted.Refresh()
2256
2257 pat = gmPerson.gmCurrentPatient()
2258 if pat['dob'] is not None:
2259 issue_age = year_noted - pat['dob']
2260 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2261 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2262
2263 return True
2264
2266 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2267 return True
2268
2270 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2271 return True
2272
2273
2274
2276
2278
2279 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2280
2281 self.selection_only = False
2282
2283 mp = gmMatchProvider.cMatchProvider_FixedList (
2284 aSeq = [
2285 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2286 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2287 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2288 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2289 ]
2290 )
2291 mp.setThresholds(1, 2, 4)
2292 self.matcher = mp
2293
2294 self.SetToolTipString(_(
2295 "The diagnostic classification or grading of this assessment.\n"
2296 "\n"
2297 "This documents how certain one is about this being a true diagnosis."
2298 ))
2299
2300
2301
2302 if __name__ == '__main__':
2303
2304
2306 """
2307 Test application for testing EMR struct widgets
2308 """
2309
2311 """
2312 Create test application UI
2313 """
2314 frame = wx.Frame (
2315 None,
2316 -4,
2317 'Testing EMR struct widgets',
2318 size=wx.Size(600, 400),
2319 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2320 )
2321 filemenu= wx.Menu()
2322 filemenu.AppendSeparator()
2323 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2324
2325
2326 menuBar = wx.MenuBar()
2327 menuBar.Append(filemenu,"&File")
2328
2329 frame.SetMenuBar(menuBar)
2330
2331 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2332 wx.DefaultPosition, wx.DefaultSize, 0 )
2333
2334
2335 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2336
2337
2338 self.__pat = gmPerson.gmCurrentPatient()
2339
2340 frame.Show(1)
2341 return 1
2342
2344 """
2345 Close test aplication
2346 """
2347 self.ExitMainLoop ()
2348
2358
2367
2368
2369
2370
2371
2379
2385
2387 app = wx.PyWidgetTester(size = (400, 40))
2388 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2389 app.MainLoop()
2390
2392 app = wx.PyWidgetTester(size = (400, 40))
2393 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2394
2395 app.MainLoop()
2396
2398 app = wx.PyWidgetTester(size = (200, 300))
2399 edit_health_issue(parent=app.frame, issue=None)
2400
2402 app = wx.PyWidgetTester(size = (200, 300))
2403 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2404 app.MainLoop()
2405
2407 app = wx.PyWidgetTester(size = (200, 300))
2408 edit_procedure(parent=app.frame)
2409
2410
2411 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2412
2413 gmI18N.activate_locale()
2414 gmI18N.install_domain()
2415 gmDateTime.init()
2416
2417
2418 pat = gmPersonSearch.ask_for_patient()
2419 if pat is None:
2420 print "No patient. Exiting gracefully..."
2421 sys.exit(0)
2422 gmPatSearchWidgets.set_active_patient(patient=pat)
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440 test_edit_procedure()
2441
2442
2443