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 __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
11 __license__ = "GPL v2 or later"
12
13
14 import sys
15 import time
16 import logging
17 import datetime as pydt
18
19
20
21 import wx
22
23
24
25 if __name__ == '__main__':
26 sys.path.insert(0, '../../')
27 from Gnumed.pycommon import gmI18N
28 from Gnumed.pycommon import gmExceptions
29 from Gnumed.pycommon import gmCfg
30 from Gnumed.pycommon import gmDateTime
31 from Gnumed.pycommon import gmTools
32 from Gnumed.pycommon import gmDispatcher
33 from Gnumed.pycommon import gmMatchProvider
34
35 from Gnumed.business import gmEMRStructItems
36 from Gnumed.business import gmSurgery
37 from Gnumed.business import gmPerson
38
39 from Gnumed.wxpython import gmPhraseWheel
40 from Gnumed.wxpython import gmGuiHelpers
41 from Gnumed.wxpython import gmListWidgets
42 from Gnumed.wxpython import gmEditArea
43 from Gnumed.wxpython import gmPatSearchWidgets
44
45
46 _log = logging.getLogger('gm.ui')
47
48
49
51 """Spin time in seconds."""
52 if time2spin == 0:
53 return
54 sleep_time = 0.1
55 total_rounds = int(time2spin / sleep_time)
56 if total_rounds < 1:
57 return
58 rounds = 0
59 while rounds < total_rounds:
60 wx.Yield()
61 time.sleep(sleep_time)
62 rounds += 1
63
64
65
76
77 def delete(procedure=None):
78 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
79 return True
80
81 gmDispatcher.send (
82 signal = u'statustext',
83 msg = _('Cannot delete performed procedure.'),
84 beep = True
85 )
86 return False
87
88 def refresh(lctrl):
89 procs = emr.get_performed_procedures()
90
91 items = [
92 [
93 u'%s%s' % (
94 p['clin_when'].strftime('%Y-%m-%d'),
95 gmTools.bool2subst (
96 p['is_ongoing'],
97 _(' (ongoing)'),
98 gmTools.coalesce (
99 initial = p['clin_end'],
100 instead = u'',
101 template_initial = u' - %s',
102 function_initial = ('strftime', u'%Y-%m-%d')
103 )
104 )
105 ),
106 p['clin_where'],
107 p['episode'],
108 p['performed_procedure']
109 ] for p in procs
110 ]
111 lctrl.set_string_items(items = items)
112 lctrl.set_data(data = procs)
113
114 gmListWidgets.get_choices_from_list (
115 parent = parent,
116 msg = _('\nSelect the procedure you want to edit !\n'),
117 caption = _('Editing performed procedures ...'),
118 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
119 single_selection = True,
120 edit_callback = edit,
121 new_callback = edit,
122 delete_callback = delete,
123 refresh_callback = refresh
124 )
125
137
138 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
139
140 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
141
150
152 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
153 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
154 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
155 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
156 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
157
158
159 mp = gmMatchProvider.cMatchProvider_SQL2 (
160 queries = [
161 u"""
162 SELECT DISTINCT ON (data) data, location
163 FROM (
164 SELECT
165 clin_where as data,
166 clin_where as location
167 FROM
168 clin.procedure
169 WHERE
170 clin_where %(fragment_condition)s
171
172 UNION ALL
173
174 SELECT
175 narrative as data,
176 narrative as location
177 FROM
178 clin.hospital_stay
179 WHERE
180 narrative %(fragment_condition)s
181 ) as union_result
182 ORDER BY data
183 LIMIT 25"""
184 ]
185 )
186 mp.setThresholds(2, 4, 6)
187 self._PRW_location.matcher = mp
188
189
190 mp = gmMatchProvider.cMatchProvider_SQL2 (
191 queries = [
192 u"""
193 select distinct on (narrative) narrative, narrative
194 from clin.procedure
195 where narrative %(fragment_condition)s
196 order by narrative
197 limit 25
198 """ ]
199 )
200 mp.setThresholds(2, 4, 6)
201 self._PRW_procedure.matcher = mp
202
204 stay = self._PRW_hospital_stay.GetData()
205 if stay is None:
206 self._PRW_hospital_stay.SetText()
207 self._PRW_location.Enable(True)
208 self._PRW_episode.Enable(True)
209 self._LBL_hospital_details.SetLabel(u'')
210 else:
211 self._PRW_location.SetText()
212 self._PRW_location.Enable(False)
213 self._PRW_episode.SetText()
214 self._PRW_episode.Enable(False)
215 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
216
218 if self._PRW_location.GetValue().strip() == u'':
219 self._PRW_hospital_stay.Enable(True)
220
221 else:
222 self._PRW_hospital_stay.SetText()
223 self._PRW_hospital_stay.Enable(False)
224 self._PRW_hospital_stay.display_as_valid(True)
225
226
238
261
262
263
321
356
358 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
359
360 if self._DPRW_end.GetData() is None:
361 self.data['clin_end'] = None
362 else:
363 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
364
365 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
366
367 if self._PRW_hospital_stay.GetData() is None:
368 self.data['pk_hospital_stay'] = None
369 self.data['clin_where'] = self._PRW_location.GetValue().strip()
370 self.data['pk_episode'] = self._PRW_episode.GetData()
371 else:
372 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
373 self.data['clin_where'] = None
374 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
375 self.data['pk_episode'] = stay['pk_episode']
376
377 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
378
379 self.data.save()
380 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
381
382 return True
383
385 self._DPRW_date.SetText()
386 self._DPRW_end.SetText()
387 self._CHBOX_ongoing.SetValue(False)
388 self._CHBOX_ongoing.Enable(True)
389 self._PRW_hospital_stay.SetText()
390 self._PRW_location.SetText()
391 self._PRW_episode.SetText()
392 self._PRW_procedure.SetText()
393 self._PRW_codes.SetText()
394
395 self._PRW_procedure.SetFocus()
396
427
439
440
441
446
462
463
464
475
476 def delete(stay=None):
477 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
478 return True
479 gmDispatcher.send (
480 signal = u'statustext',
481 msg = _('Cannot delete hospitalization.'),
482 beep = True
483 )
484 return False
485
486 def refresh(lctrl):
487 stays = emr.get_hospital_stays()
488 items = [
489 [
490 s['admission'].strftime('%Y-%m-%d'),
491 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
492 s['episode'],
493 gmTools.coalesce(s['hospital'], u'')
494 ] for s in stays
495 ]
496 lctrl.set_string_items(items = items)
497 lctrl.set_data(data = stays)
498
499 gmListWidgets.get_choices_from_list (
500 parent = parent,
501 msg = _("The patient's hospitalizations:\n"),
502 caption = _('Editing hospitalizations ...'),
503 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
504 single_selection = True,
505 edit_callback = edit,
506 new_callback = edit,
507 delete_callback = delete,
508 refresh_callback = refresh
509 )
510
511
523
525 """Phrasewheel to allow selection of a hospitalization."""
527
528 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
529
530 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
531
532 mp = gmMatchProvider.cMatchProvider_SQL2 (
533 queries = [
534 u"""
535 select
536 pk_hospital_stay,
537 descr
538 from (
539 select distinct on (pk_hospital_stay)
540 pk_hospital_stay,
541 descr
542 from
543 (select
544 pk_hospital_stay,
545 (
546 to_char(admission, 'YYYY-Mon-DD')
547 || coalesce((' (' || hospital || '):'), ': ')
548 || episode
549 || coalesce((' (' || health_issue || ')'), '')
550 ) as descr
551 from
552 clin.v_pat_hospital_stays
553 where
554 %(ctxt_pat)s
555
556 hospital %(fragment_condition)s
557 or
558 episode %(fragment_condition)s
559 or
560 health_issue %(fragment_condition)s
561 ) as the_stays
562 ) as distinct_stays
563 order by descr
564 limit 25
565 """ ],
566 context = ctxt
567 )
568 mp.setThresholds(3, 4, 6)
569 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
570
571 self.matcher = mp
572 self.selection_only = True
573
574 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
575
576 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
577
581
582
583
585
586 valid = True
587
588 if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
589 valid = False
590 gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospitalization.'), beep = True)
591
592 if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
593 if self._PRW_discharge.date is not None:
594 if self._PRW_admission.date is not None:
595 if not self._PRW_discharge.date > self._PRW_admission.date:
596 valid = False
597 self._PRW_discharge.display_as_valid(False)
598 gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospitalization.'), beep = True)
599
600 if self._PRW_episode.GetValue().strip() == u'':
601 valid = False
602 self._PRW_episode.display_as_valid(False)
603 gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospitalization.'), beep = True)
604
605 return (valid is True)
606
619
629
635
645
647 print "this was not expected to be used in this edit area"
648
649
650
659
660 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
661
663 if parent is None:
664 parent = wx.GetApp().GetTopWindow()
665
666
667 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
668 if dlg.ShowModal() == wx.ID_OK:
669 dlg.Destroy()
670 return True
671 dlg.Destroy()
672 return False
673
676
677 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
678
679 if patient is None:
680 patient = gmPerson.gmCurrentPatient()
681
682 if not patient.connected:
683 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
684 return False
685
686 if parent is None:
687 parent = wx.GetApp().GetTopWindow()
688
689 emr = patient.get_emr()
690
691
692 def refresh(lctrl):
693 if encounters is None:
694 encs = emr.get_encounters()
695 else:
696 encs = encounters
697
698 items = [
699 [
700 e['started'].strftime('%x %H:%M'),
701 e['last_affirmed'].strftime('%H:%M'),
702 e['l10n_type'],
703 gmTools.coalesce(e['reason_for_encounter'], u''),
704 gmTools.coalesce(e['assessment_of_encounter'], u''),
705 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
706 e['pk_encounter']
707 ] for e in encs
708 ]
709 lctrl.set_string_items(items = items)
710 lctrl.set_data(data = encs)
711 active_pk = emr.active_encounter['pk_encounter']
712 for idx in range(len(encs)):
713 e = encs[idx]
714 if e['pk_encounter'] == active_pk:
715 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
716
717 def new():
718 cfg_db = gmCfg.cCfgSQL()
719
720 enc_type = cfg_db.get2 (
721 option = u'encounter.default_type',
722 workplace = gmSurgery.gmCurrentPractice().active_workplace,
723 bias = u'user',
724 default = u'in surgery'
725 )
726 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
727 return edit_encounter(parent = parent, encounter = enc)
728
729 def edit(enc=None):
730 return edit_encounter(parent = parent, encounter = enc)
731
732 def edit_active(enc=None):
733 return edit_encounter(parent = parent, encounter = emr.active_encounter)
734
735 def start_new(enc=None):
736 start_new_encounter(emr = emr)
737 return True
738
739 return gmListWidgets.get_choices_from_list (
740 parent = parent,
741 msg = _("The patient's encounters.\n"),
742 caption = _('Encounters ...'),
743 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
744 can_return_empty = False,
745 single_selection = single_selection,
746 refresh_callback = refresh,
747 edit_callback = edit,
748 new_callback = new,
749 ignore_OK_button = ignore_OK_button,
750 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
751 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
752 )
753
755 """This is used as the callback when the EMR detects that the
756 patient was here rather recently and wants to ask the
757 provider whether to continue the recent encounter.
758 """
759 if parent is None:
760 parent = wx.GetApp().GetTopWindow()
761
762 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
763 parent = None,
764 id = -1,
765 caption = caption,
766 question = msg,
767 button_defs = [
768 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
769 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
770 ],
771 show_checkbox = False
772 )
773
774 result = dlg.ShowModal()
775 dlg.Destroy()
776
777 if result == wx.ID_YES:
778 return True
779
780 return False
781
783
784 if parent is None:
785 parent = wx.GetApp().GetTopWindow()
786
787
788 def edit(enc_type=None):
789 return edit_encounter_type(parent = parent, encounter_type = enc_type)
790
791 def delete(enc_type=None):
792 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
793 return True
794 gmDispatcher.send (
795 signal = u'statustext',
796 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
797 beep = True
798 )
799 return False
800
801 def refresh(lctrl):
802 enc_types = gmEMRStructItems.get_encounter_types()
803 lctrl.set_string_items(items = enc_types)
804
805 gmListWidgets.get_choices_from_list (
806 parent = parent,
807 msg = _('\nSelect the encounter type you want to edit !\n'),
808 caption = _('Managing encounter types ...'),
809 columns = [_('Local name'), _('Encounter type')],
810 single_selection = True,
811 edit_callback = edit,
812 new_callback = edit,
813 delete_callback = delete,
814 refresh_callback = refresh
815 )
816
826
828
830 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
831
832 cmd = u"""
833 SELECT DISTINCT ON (list_label)
834 pk_encounter
835 AS data,
836 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type || ' [#' || pk_encounter || ']'
837 AS list_label,
838 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
839 AS field_label
840 FROM
841 clin.v_pat_encounters
842 WHERE
843 (
844 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s
845 OR
846 l10n_type %(fragment_condition)s
847 OR
848 type %(fragment_condition)s
849 ) %(ctxt_patient)s
850 ORDER BY
851 list_label
852 LIMIT
853 30
854 """
855 context = {'ctxt_patient': {
856 'where_part': u'AND pk_patient = %(patient)s',
857 'placeholder': u'patient'
858 }}
859
860 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context)
861 self.matcher._SQL_data2match = u"""
862 SELECT
863 pk_encounter
864 AS data,
865 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type
866 AS list_label,
867 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
868 AS field_label
869 FROM
870 clin.v_pat_encounters
871 WHERE
872 pk_encounter = %(pk)s
873 """
874 self.matcher.setThresholds(1, 3, 5)
875
876 self.selection_only = True
877
878 self.set_context(context = 'patient', val = None)
879
886
897
899 """Phrasewheel to allow selection of encounter type.
900
901 - user input interpreted as encounter type in English or local language
902 - data returned is pk of corresponding encounter type or None
903 """
905
906 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
907
908 mp = gmMatchProvider.cMatchProvider_SQL2 (
909 queries = [
910 u"""
911 SELECT
912 data,
913 field_label,
914 list_label
915 FROM (
916 SELECT DISTINCT ON (data) *
917 FROM (
918 SELECT
919 pk AS data,
920 _(description) AS field_label,
921 case
922 when _(description) = description then _(description)
923 else _(description) || ' (' || description || ')'
924 end AS list_label
925 FROM
926 clin.encounter_type
927 WHERE
928 _(description) %(fragment_condition)s
929 OR
930 description %(fragment_condition)s
931 ) AS q_distinct_pk
932 ) AS q_ordered
933 ORDER BY
934 list_label
935 """ ]
936 )
937 mp.setThresholds(2, 4, 6)
938
939 self.matcher = mp
940 self.selection_only = True
941 self.picklist_delay = 50
942
943 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
944
946
951
952
953
954
955
985
998
1008
1010 self._TCTRL_l10n_name.SetValue(u'')
1011 self._TCTRL_name.SetValue(u'')
1012 self._TCTRL_name.Enable(True)
1013
1015 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
1016 self._TCTRL_name.SetValue(self.data['description'])
1017
1018 self._TCTRL_name.Enable(False)
1019
1021 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
1022 self._TCTRL_name.SetValue(self.data['description'])
1023 self._TCTRL_name.Enable(True)
1024
1025
1026
1027
1028
1029
1030 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
1031
1033
1035 try:
1036 self.__encounter = kwargs['encounter']
1037 del kwargs['encounter']
1038 except KeyError:
1039 self.__encounter = None
1040
1041 try:
1042 msg = kwargs['msg']
1043 del kwargs['msg']
1044 except KeyError:
1045 msg = None
1046
1047 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
1048
1049 self.refresh(msg = msg)
1050
1051
1052
1053 - def refresh(self, encounter=None, msg=None):
1054
1055 if msg is not None:
1056 self._LBL_instructions.SetLabel(msg)
1057
1058 if encounter is not None:
1059 self.__encounter = encounter
1060
1061 if self.__encounter is None:
1062 return True
1063
1064
1065
1066 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
1067 self._LBL_patient.SetLabel(pat.get_description_gender())
1068
1069 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
1070
1071 fts = gmDateTime.cFuzzyTimestamp (
1072 timestamp = self.__encounter['started'],
1073 accuracy = gmDateTime.acc_minutes
1074 )
1075 self._PRW_start.SetText(fts.format_accurately(), data=fts)
1076
1077 fts = gmDateTime.cFuzzyTimestamp (
1078 timestamp = self.__encounter['last_affirmed'],
1079 accuracy = gmDateTime.acc_minutes
1080 )
1081 self._PRW_end.SetText(fts.format_accurately(), data=fts)
1082
1083
1084 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
1085 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
1086 self._PRW_rfe_codes.SetText(val, data)
1087
1088
1089 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
1090 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
1091 self._PRW_aoe_codes.SetText(val, data)
1092
1093
1094 if self.__encounter['last_affirmed'] == self.__encounter['started']:
1095 self._PRW_end.SetFocus()
1096 else:
1097 self._TCTRL_aoe.SetFocus()
1098
1099 return True
1100
1102
1103 if self._PRW_encounter_type.GetData() is None:
1104 self._PRW_encounter_type.SetBackgroundColour('pink')
1105 self._PRW_encounter_type.Refresh()
1106 self._PRW_encounter_type.SetFocus()
1107 return False
1108 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1109 self._PRW_encounter_type.Refresh()
1110
1111
1112 if self._PRW_start.GetValue().strip() == u'':
1113 self._PRW_start.SetBackgroundColour('pink')
1114 self._PRW_start.Refresh()
1115 self._PRW_start.SetFocus()
1116 return False
1117 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False):
1118 self._PRW_start.SetBackgroundColour('pink')
1119 self._PRW_start.Refresh()
1120 self._PRW_start.SetFocus()
1121 return False
1122 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1123 self._PRW_start.Refresh()
1124
1125
1126
1127
1128
1129
1130
1131 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False):
1132 self._PRW_end.SetBackgroundColour('pink')
1133 self._PRW_end.Refresh()
1134 self._PRW_end.SetFocus()
1135 return False
1136 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1137 self._PRW_end.Refresh()
1138
1139 return True
1140
1142 if not self.__is_valid_for_save():
1143 return False
1144
1145 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1146 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1147 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1148 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1149 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1150 self.__encounter.save_payload()
1151
1152 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1153 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1154
1155 return True
1156
1157
1159
1161 encounter = kwargs['encounter']
1162 del kwargs['encounter']
1163
1164 try:
1165 button_defs = kwargs['button_defs']
1166 del kwargs['button_defs']
1167 except KeyError:
1168 button_defs = None
1169
1170 try:
1171 msg = kwargs['msg']
1172 del kwargs['msg']
1173 except KeyError:
1174 msg = None
1175
1176 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1177 self.SetSize((450, 280))
1178 self.SetMinSize((450, 280))
1179
1180 if button_defs is not None:
1181 self._BTN_save.SetLabel(button_defs[0][0])
1182 self._BTN_save.SetToolTipString(button_defs[0][1])
1183 self._BTN_close.SetLabel(button_defs[1][0])
1184 self._BTN_close.SetToolTipString(button_defs[1][1])
1185 self.Refresh()
1186
1187 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1188
1189 self.Fit()
1190
1197
1198 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
1199
1201
1206
1208 self._TCTRL_encounter.SetValue(u'')
1209 self._TCTRL_encounter.SetToolTipString(u'')
1210 self._BTN_new.Enable(False)
1211 self._BTN_list.Enable(False)
1212
1214 pat = gmPerson.gmCurrentPatient()
1215 if not pat.connected:
1216 self.clear()
1217 return
1218
1219 enc = pat.get_emr().active_encounter
1220 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
1221 self._TCTRL_encounter.SetToolTipString (
1222 _('The active encounter of the current patient:\n\n%s') %
1223 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
1224 )
1225 self._BTN_new.Enable(True)
1226 self._BTN_list.Enable(True)
1227
1229 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
1230
1231 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
1232
1233
1234 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
1235 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
1236 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1237
1238
1239
1241 wx.CallAfter(self.clear)
1242
1244 wx.CallAfter(self.refresh)
1245 return True
1246
1252
1258
1263
1264
1265
1275
1345
1347 """Prepare changing health issue for an episode.
1348
1349 Checks for two-open-episodes conflict. When this
1350 function succeeds, the pk_health_issue has been set
1351 on the episode instance and the episode should - for
1352 all practical purposes - be ready for save_payload().
1353 """
1354
1355 if not episode['episode_open']:
1356 episode['pk_health_issue'] = target_issue['pk_health_issue']
1357 if save_to_backend:
1358 episode.save_payload()
1359 return True
1360
1361
1362 if target_issue is None:
1363 episode['pk_health_issue'] = None
1364 if save_to_backend:
1365 episode.save_payload()
1366 return True
1367
1368
1369 db_cfg = gmCfg.cCfgSQL()
1370 epi_ttl = int(db_cfg.get2 (
1371 option = u'episode.ttl',
1372 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1373 bias = 'user',
1374 default = 60
1375 ))
1376 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1377 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1378 existing_epi = target_issue.get_open_episode()
1379
1380
1381 if existing_epi is None:
1382 episode['pk_health_issue'] = target_issue['pk_health_issue']
1383 if save_to_backend:
1384 episode.save_payload()
1385 return True
1386
1387
1388 if existing_epi['pk_episode'] == episode['pk_episode']:
1389 episode['pk_health_issue'] = target_issue['pk_health_issue']
1390 if save_to_backend:
1391 episode.save_payload()
1392 return True
1393
1394
1395 move_range = episode.get_access_range()
1396 exist_range = existing_epi.get_access_range()
1397 question = _(
1398 'You want to associate the running episode:\n\n'
1399 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1400 'with the health issue:\n\n'
1401 ' "%(issue_name)s"\n\n'
1402 'There already is another episode running\n'
1403 'for this health issue:\n\n'
1404 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1405 'However, there can only be one running\n'
1406 'episode per health issue.\n\n'
1407 'Which episode do you want to close ?'
1408 ) % {
1409 'new_epi_name': episode['description'],
1410 'new_epi_start': move_range[0].strftime('%m/%y'),
1411 'new_epi_end': move_range[1].strftime('%m/%y'),
1412 'issue_name': target_issue['description'],
1413 'old_epi_name': existing_epi['description'],
1414 'old_epi_start': exist_range[0].strftime('%m/%y'),
1415 'old_epi_end': exist_range[1].strftime('%m/%y')
1416 }
1417 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1418 parent = None,
1419 id = -1,
1420 caption = _('Resolving two-running-episodes conflict'),
1421 question = question,
1422 button_defs = [
1423 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1424 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1425 ]
1426 )
1427 decision = dlg.ShowModal()
1428
1429 if decision == wx.ID_CANCEL:
1430
1431 return False
1432
1433 elif decision == wx.ID_YES:
1434
1435 existing_epi['episode_open'] = False
1436 existing_epi.save_payload()
1437
1438 elif decision == wx.ID_NO:
1439
1440 episode['episode_open'] = False
1441
1442 else:
1443 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1444
1445 episode['pk_health_issue'] = target_issue['pk_health_issue']
1446 if save_to_backend:
1447 episode.save_payload()
1448 return True
1449
1473
1475 """Let user select an episode *description*.
1476
1477 The user can select an episode description from the previously
1478 used descriptions across all episodes across all patients.
1479
1480 Selection is done with a phrasewheel so the user can
1481 type the episode name and matches will be shown. Typing
1482 "*" will show the entire list of episodes.
1483
1484 If the user types a description not existing yet a
1485 new episode description will be returned.
1486 """
1488
1489 mp = gmMatchProvider.cMatchProvider_SQL2 (
1490 queries = [
1491 u"""
1492 SELECT DISTINCT ON (description)
1493 description
1494 AS data,
1495 description
1496 AS field_label,
1497 description || ' ('
1498 || CASE
1499 WHEN is_open IS TRUE THEN _('ongoing')
1500 ELSE _('closed')
1501 END
1502 || ')'
1503 AS list_label
1504 FROM
1505 clin.episode
1506 WHERE
1507 description %(fragment_condition)s
1508 ORDER BY description
1509 LIMIT 30
1510 """
1511 ]
1512 )
1513 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1514 self.matcher = mp
1515
1517 """Let user select an episode.
1518
1519 The user can select an episode from the existing episodes of a
1520 patient. Selection is done with a phrasewheel so the user
1521 can type the episode name and matches will be shown. Typing
1522 "*" will show the entire list of episodes. Closed episodes
1523 will be marked as such. If the user types an episode name not
1524 in the list of existing episodes a new episode can be created
1525 from it if the programmer activated that feature.
1526
1527 If keyword <patient_id> is set to None or left out the control
1528 will listen to patient change signals and therefore act on
1529 gmPerson.gmCurrentPatient() changes.
1530 """
1532
1533 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1534
1535 mp = gmMatchProvider.cMatchProvider_SQL2 (
1536 queries = [
1537 u"""(
1538
1539 select
1540 pk_episode
1541 as data,
1542 description
1543 as field_label,
1544 coalesce (
1545 description || ' - ' || health_issue,
1546 description
1547 ) as list_label,
1548 1 as rank
1549 from
1550 clin.v_pat_episodes
1551 where
1552 episode_open is true and
1553 description %(fragment_condition)s
1554 %(ctxt_pat)s
1555
1556 ) union all (
1557
1558 select
1559 pk_episode
1560 as data,
1561 description
1562 as field_label,
1563 coalesce (
1564 description || _(' (closed)') || ' - ' || health_issue,
1565 description || _(' (closed)')
1566 ) as list_label,
1567 2 as rank
1568 from
1569 clin.v_pat_episodes
1570 where
1571 description %(fragment_condition)s and
1572 episode_open is false
1573 %(ctxt_pat)s
1574
1575 )
1576
1577 order by rank, list_label
1578 limit 30"""
1579 ],
1580 context = ctxt
1581 )
1582
1583 try:
1584 kwargs['patient_id']
1585 except KeyError:
1586 kwargs['patient_id'] = None
1587
1588 if kwargs['patient_id'] is None:
1589 self.use_current_patient = True
1590 self.__register_patient_change_signals()
1591 pat = gmPerson.gmCurrentPatient()
1592 if pat.connected:
1593 mp.set_context('pat', pat.ID)
1594 else:
1595 self.use_current_patient = False
1596 self.__patient_id = int(kwargs['patient_id'])
1597 mp.set_context('pat', self.__patient_id)
1598
1599 del kwargs['patient_id']
1600
1601 gmPhraseWheel.cPhraseWheel.__init__ (
1602 self,
1603 *args,
1604 **kwargs
1605 )
1606 self.matcher = mp
1607
1608
1609
1611 if self.use_current_patient:
1612 return False
1613 self.__patient_id = int(patient_id)
1614 self.set_context('pat', self.__patient_id)
1615 return True
1616
1617 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1620
1622
1623 epi_name = self.GetValue().strip()
1624 if epi_name == u'':
1625 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1626 _log.debug('cannot create episode without name')
1627 return
1628
1629 if self.use_current_patient:
1630 pat = gmPerson.gmCurrentPatient()
1631 else:
1632 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1633
1634 emr = pat.get_emr()
1635 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1636 if epi is None:
1637 self.data = {}
1638 else:
1639 self.SetText (
1640 value = epi_name,
1641 data = epi['pk_episode']
1642 )
1643
1646
1647
1648
1652
1655
1657 if self.use_current_patient:
1658 patient = gmPerson.gmCurrentPatient()
1659 self.set_context('pat', patient.ID)
1660 return True
1661
1662 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1663
1664 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1665
1678
1679
1680
1682
1683 errors = False
1684
1685 if len(self._PRW_description.GetValue().strip()) == 0:
1686 errors = True
1687 self._PRW_description.display_as_valid(False)
1688 self._PRW_description.SetFocus()
1689 else:
1690 self._PRW_description.display_as_valid(True)
1691 self._PRW_description.Refresh()
1692
1693 return not errors
1694
1696
1697 pat = gmPerson.gmCurrentPatient()
1698 emr = pat.get_emr()
1699
1700 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1701 epi['summary'] = self._TCTRL_status.GetValue().strip()
1702 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1703 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1704
1705 issue_name = self._PRW_issue.GetValue().strip()
1706 if len(issue_name) != 0:
1707 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1708 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1709
1710 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1711 gmDispatcher.send (
1712 signal = 'statustext',
1713 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1714 epi['description'],
1715 issue['description']
1716 )
1717 )
1718 gmEMRStructItems.delete_episode(episode = epi)
1719 return False
1720
1721 epi.save()
1722
1723 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1724
1725 self.data = epi
1726 return True
1727
1729
1730 self.data['description'] = self._PRW_description.GetValue().strip()
1731 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1732 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1733 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1734
1735 issue_name = self._PRW_issue.GetValue().strip()
1736 if len(issue_name) == 0:
1737 self.data['pk_health_issue'] = None
1738 else:
1739 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1740 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1741
1742 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1743 gmDispatcher.send (
1744 signal = 'statustext',
1745 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1746 self.data['description'],
1747 issue['description']
1748 )
1749 )
1750 return False
1751
1752 self.data.save()
1753 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1754
1755 return True
1756
1769
1788
1790 self._refresh_as_new()
1791
1792
1793
1803
1805
1806 if parent is None:
1807 parent = wx.GetApp().GetTopWindow()
1808
1809 def refresh(lctrl):
1810 issues = emr.get_health_issues()
1811 items = [
1812 [
1813 gmTools.bool2subst(i['is_confidential'], _('CONFIDENTIAL'), u'', u''),
1814 i['description'],
1815 gmTools.bool2subst(i['clinically_relevant'], _('relevant'), u'', u''),
1816 gmTools.bool2subst(i['is_active'], _('active'), u'', u''),
1817 gmTools.bool2subst(i['is_cause_of_death'], _('fatal'), u'', u'')
1818 ] for i in issues
1819 ]
1820 lctrl.set_string_items(items = items)
1821 lctrl.set_data(data = issues)
1822
1823 return gmListWidgets.get_choices_from_list (
1824 parent = parent,
1825 msg = _('\nSelect the health issues !\n'),
1826 caption = _('Showing health issues ...'),
1827 columns = [u'', _('Health issue'), u'', u'', u''],
1828 single_selection = False,
1829
1830
1831
1832 refresh_callback = refresh
1833 )
1834
1836
1837
1838
1840
1841 issues = kwargs['issues']
1842 del kwargs['issues']
1843
1844 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1845
1846 self.SetTitle(_('Select the health issues you are interested in ...'))
1847 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1848
1849 for issue in issues:
1850 if issue['is_confidential']:
1851 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1852 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1853 else:
1854 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1855
1856 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1857 if issue['clinically_relevant']:
1858 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1859 if issue['is_active']:
1860 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1861 if issue['is_cause_of_death']:
1862 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1863
1864 self._LCTRL_items.set_column_widths()
1865 self._LCTRL_items.set_data(data = issues)
1866
1868 """Let the user select a health issue.
1869
1870 The user can select a health issue from the existing issues
1871 of a patient. Selection is done with a phrasewheel so the user
1872 can type the issue name and matches will be shown. Typing
1873 "*" will show the entire list of issues. Inactive issues
1874 will be marked as such. If the user types an issue name not
1875 in the list of existing issues a new issue can be created
1876 from it if the programmer activated that feature.
1877
1878 If keyword <patient_id> is set to None or left out the control
1879 will listen to patient change signals and therefore act on
1880 gmPerson.gmCurrentPatient() changes.
1881 """
1883
1884 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1885
1886 mp = gmMatchProvider.cMatchProvider_SQL2 (
1887
1888 queries = [
1889 u"""
1890 SELECT
1891 data,
1892 field_label,
1893 list_label
1894 FROM ((
1895 SELECT
1896 pk_health_issue AS data,
1897 description AS field_label,
1898 description AS list_label
1899 FROM clin.v_health_issues
1900 WHERE
1901 is_active IS true
1902 AND
1903 description %(fragment_condition)s
1904 AND
1905 %(ctxt_pat)s
1906
1907 ) UNION (
1908
1909 SELECT
1910 pk_health_issue AS data,
1911 description AS field_label,
1912 description || _(' (inactive)') AS list_label
1913 FROM clin.v_health_issues
1914 WHERE
1915 is_active IS false
1916 AND
1917 description %(fragment_condition)s
1918 AND
1919 %(ctxt_pat)s
1920 )) AS union_query
1921 ORDER BY
1922 list_label"""],
1923 context = ctxt
1924 )
1925
1926 try: kwargs['patient_id']
1927 except KeyError: kwargs['patient_id'] = None
1928
1929 if kwargs['patient_id'] is None:
1930 self.use_current_patient = True
1931 self.__register_patient_change_signals()
1932 pat = gmPerson.gmCurrentPatient()
1933 if pat.connected:
1934 mp.set_context('pat', pat.ID)
1935 else:
1936 self.use_current_patient = False
1937 self.__patient_id = int(kwargs['patient_id'])
1938 mp.set_context('pat', self.__patient_id)
1939
1940 del kwargs['patient_id']
1941
1942 gmPhraseWheel.cPhraseWheel.__init__ (
1943 self,
1944 *args,
1945 **kwargs
1946 )
1947 self.matcher = mp
1948
1949
1950
1952 if self.use_current_patient:
1953 return False
1954 self.__patient_id = int(patient_id)
1955 self.set_context('pat', self.__patient_id)
1956 return True
1957
1959 issue_name = self.GetValue().strip()
1960 if issue_name == u'':
1961 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create health issue without name.'), beep = True)
1962 _log.debug('cannot create health issue without name')
1963 return
1964
1965 if self.use_current_patient:
1966 pat = gmPerson.gmCurrentPatient()
1967 else:
1968 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1969
1970 emr = pat.get_emr()
1971 issue = emr.add_health_issue(issue_name = issue_name)
1972
1973 if issue is None:
1974 self.data = {}
1975 else:
1976 self.SetText (
1977 value = issue_name,
1978 data = issue['pk_health_issue']
1979 )
1980
1983
1984
1985
1989
1992
1994 if self.use_current_patient:
1995 patient = gmPerson.gmCurrentPatient()
1996 self.set_context('pat', patient.ID)
1997 return True
1998
1999 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg
2000
2002
2004 try:
2005 msg = kwargs['message']
2006 except KeyError:
2007 msg = None
2008 del kwargs['message']
2009 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
2010 if msg is not None:
2011 self._lbl_message.SetLabel(label=msg)
2012
2023
2024 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
2025
2026 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
2027 """Panel encapsulating health issue edit area functionality."""
2028
2030
2031 try:
2032 issue = kwargs['issue']
2033 except KeyError:
2034 issue = None
2035
2036 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
2037
2038 gmEditArea.cGenericEditAreaMixin.__init__(self)
2039
2040
2041 mp = gmMatchProvider.cMatchProvider_SQL2 (
2042 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
2043 )
2044 mp.setThresholds(1, 3, 5)
2045 self._PRW_condition.matcher = mp
2046
2047 mp = gmMatchProvider.cMatchProvider_SQL2 (
2048 queries = [u"""
2049 select distinct on (grouping) grouping, grouping from (
2050
2051 select rank, grouping from ((
2052
2053 select
2054 grouping,
2055 1 as rank
2056 from
2057 clin.health_issue
2058 where
2059 grouping %%(fragment_condition)s
2060 and
2061 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
2062
2063 ) union (
2064
2065 select
2066 grouping,
2067 2 as rank
2068 from
2069 clin.health_issue
2070 where
2071 grouping %%(fragment_condition)s
2072
2073 )) as union_result
2074
2075 order by rank
2076
2077 ) as order_result
2078
2079 limit 50""" % gmPerson.gmCurrentPatient().ID
2080 ]
2081 )
2082 mp.setThresholds(1, 3, 5)
2083 self._PRW_grouping.matcher = mp
2084
2085 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
2086 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
2087
2088 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
2089 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
2090
2091 self._PRW_year_noted.Enable(True)
2092
2093 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
2094
2095 self.data = issue
2096
2097
2098
2118
2120 pat = gmPerson.gmCurrentPatient()
2121 emr = pat.get_emr()
2122
2123 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
2124
2125 side = u''
2126 if self._ChBOX_left.GetValue():
2127 side += u's'
2128 if self._ChBOX_right.GetValue():
2129 side += u'd'
2130 issue['laterality'] = side
2131
2132 issue['summary'] = self._TCTRL_status.GetValue().strip()
2133 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2134 issue['grouping'] = self._PRW_grouping.GetValue().strip()
2135 issue['is_active'] = self._ChBOX_active.GetValue()
2136 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
2137 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
2138 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
2139
2140 age_noted = self._PRW_age_noted.GetData()
2141 if age_noted is not None:
2142 issue['age_noted'] = age_noted
2143
2144 issue.save()
2145
2146 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2147
2148 self.data = issue
2149 return True
2150
2152
2153 self.data['description'] = self._PRW_condition.GetValue().strip()
2154
2155 side = u''
2156 if self._ChBOX_left.GetValue():
2157 side += u's'
2158 if self._ChBOX_right.GetValue():
2159 side += u'd'
2160 self.data['laterality'] = side
2161
2162 self.data['summary'] = self._TCTRL_status.GetValue().strip()
2163 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2164 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
2165 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
2166 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
2167 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
2168 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
2169
2170 age_noted = self._PRW_age_noted.GetData()
2171 if age_noted is not None:
2172 self.data['age_noted'] = age_noted
2173
2174 self.data.save()
2175 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2176
2177 return True
2178
2180 self._PRW_condition.SetText()
2181 self._ChBOX_left.SetValue(0)
2182 self._ChBOX_right.SetValue(0)
2183 self._PRW_codes.SetText()
2184 self._on_leave_codes()
2185 self._PRW_certainty.SetText()
2186 self._PRW_grouping.SetText()
2187 self._TCTRL_status.SetValue(u'')
2188 self._PRW_age_noted.SetText()
2189 self._PRW_year_noted.SetText()
2190 self._ChBOX_active.SetValue(0)
2191 self._ChBOX_relevant.SetValue(1)
2192 self._ChBOX_confidential.SetValue(0)
2193 self._ChBOX_caused_death.SetValue(0)
2194
2195 return True
2196
2237
2239 return self._refresh_as_new()
2240
2241
2242
2244 if not self._PRW_codes.IsModified():
2245 return True
2246
2247 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2248
2250
2251 if not self._PRW_age_noted.IsModified():
2252 return True
2253
2254 str_age = self._PRW_age_noted.GetValue().strip()
2255
2256 if str_age == u'':
2257 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2258 return True
2259
2260 age = gmDateTime.str2interval(str_interval = str_age)
2261
2262 if age is None:
2263 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
2264 self._PRW_age_noted.SetBackgroundColour('pink')
2265 self._PRW_age_noted.Refresh()
2266 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2267 return True
2268
2269 pat = gmPerson.gmCurrentPatient()
2270 if pat['dob'] is not None:
2271 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2272
2273 if age >= max_age:
2274 gmDispatcher.send (
2275 signal = 'statustext',
2276 msg = _(
2277 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2278 ) % (age, pat.get_medical_age())
2279 )
2280 self._PRW_age_noted.SetBackgroundColour('pink')
2281 self._PRW_age_noted.Refresh()
2282 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2283 return True
2284
2285 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2286 self._PRW_age_noted.Refresh()
2287 self._PRW_age_noted.SetData(data=age)
2288
2289 if pat['dob'] is not None:
2290 fts = gmDateTime.cFuzzyTimestamp (
2291 timestamp = pat['dob'] + age,
2292 accuracy = gmDateTime.acc_months
2293 )
2294 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2295
2296
2297
2298
2299
2300 return True
2301
2303
2304 if not self._PRW_year_noted.IsModified():
2305 return True
2306
2307 year_noted = self._PRW_year_noted.GetData()
2308
2309 if year_noted is None:
2310 if self._PRW_year_noted.GetValue().strip() == u'':
2311 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2312 return True
2313 self._PRW_year_noted.SetBackgroundColour('pink')
2314 self._PRW_year_noted.Refresh()
2315 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2316 return True
2317
2318 year_noted = year_noted.get_pydt()
2319
2320 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2321 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2322 self._PRW_year_noted.SetBackgroundColour('pink')
2323 self._PRW_year_noted.Refresh()
2324 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2325 return True
2326
2327 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2328 self._PRW_year_noted.Refresh()
2329
2330 pat = gmPerson.gmCurrentPatient()
2331 if pat['dob'] is not None:
2332 issue_age = year_noted - pat['dob']
2333 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2334 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2335
2336 return True
2337
2339 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2340 return True
2341
2343 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2344 return True
2345
2346
2347
2349
2351
2352 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2353
2354 self.selection_only = False
2355
2356 mp = gmMatchProvider.cMatchProvider_FixedList (
2357 aSeq = [
2358 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2359 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2360 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2361 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2362 ]
2363 )
2364 mp.setThresholds(1, 2, 4)
2365 self.matcher = mp
2366
2367 self.SetToolTipString(_(
2368 "The diagnostic classification or grading of this assessment.\n"
2369 "\n"
2370 "This documents how certain one is about this being a true diagnosis."
2371 ))
2372
2373
2374
2375 if __name__ == '__main__':
2376
2377 from Gnumed.business import gmPersonSearch
2378
2379
2381 """
2382 Test application for testing EMR struct widgets
2383 """
2384
2386 """
2387 Create test application UI
2388 """
2389 frame = wx.Frame (
2390 None,
2391 -4,
2392 'Testing EMR struct widgets',
2393 size=wx.Size(600, 400),
2394 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2395 )
2396 filemenu= wx.Menu()
2397 filemenu.AppendSeparator()
2398 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2399
2400
2401 menuBar = wx.MenuBar()
2402 menuBar.Append(filemenu,"&File")
2403
2404 frame.SetMenuBar(menuBar)
2405
2406 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2407 wx.DefaultPosition, wx.DefaultSize, 0 )
2408
2409
2410 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2411
2412
2413 self.__pat = gmPerson.gmCurrentPatient()
2414
2415 frame.Show(1)
2416 return 1
2417
2419 """
2420 Close test aplication
2421 """
2422 self.ExitMainLoop ()
2423
2433
2442
2443
2444
2445
2446
2454
2460
2462 app = wx.PyWidgetTester(size = (400, 40))
2463 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2464 app.MainLoop()
2465
2467 app = wx.PyWidgetTester(size = (400, 40))
2468 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2469
2470 app.MainLoop()
2471
2473 app = wx.PyWidgetTester(size = (200, 300))
2474 edit_health_issue(parent=app.frame, issue=None)
2475
2477 app = wx.PyWidgetTester(size = (200, 300))
2478 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2479 app.MainLoop()
2480
2482 app = wx.PyWidgetTester(size = (200, 300))
2483 edit_procedure(parent=app.frame)
2484
2485
2486 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2487
2488 gmI18N.activate_locale()
2489 gmI18N.install_domain()
2490 gmDateTime.init()
2491
2492
2493 pat = gmPersonSearch.ask_for_patient()
2494 if pat is None:
2495 print "No patient. Exiting gracefully..."
2496 sys.exit(0)
2497 gmPatSearchWidgets.set_active_patient(patient=pat)
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515 test_edit_procedure()
2516
2517
2518