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"
12
13
14 import sys, re, datetime as pydt, logging, time
15
16
17
18 import wx
19
20
21
22 if __name__ == '__main__':
23 sys.path.insert(0, '../../')
24 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
25 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery, gmPersonSearch
26 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
27
28
29 _log = logging.getLogger('gm.ui')
30
31
32
34 """Spin time in seconds."""
35 if time2spin == 0:
36 return
37 sleep_time = 0.1
38 total_rounds = int(time2spin / sleep_time)
39 if total_rounds < 1:
40 return
41 rounds = 0
42 while rounds < total_rounds:
43 wx.Yield()
44 time.sleep(sleep_time)
45 rounds += 1
46
47
48
59
60 def delete(procedure=None):
61 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
62 return True
63
64 gmDispatcher.send (
65 signal = u'statustext',
66 msg = _('Cannot delete performed procedure.'),
67 beep = True
68 )
69 return False
70
71 def refresh(lctrl):
72 procs = emr.get_performed_procedures()
73
74 items = [
75 [
76 u'%s%s' % (
77 p['clin_when'].strftime('%Y-%m-%d'),
78 gmTools.bool2subst (
79 p['is_ongoing'],
80 _(' (ongoing)'),
81 gmTools.coalesce (
82 initial = p['clin_end'],
83 instead = u'',
84 template_initial = u' - %s',
85 function_initial = ('strftime', u'%Y-%m-%d')
86 )
87 )
88 ),
89 p['clin_where'],
90 p['episode'],
91 p['performed_procedure']
92 ] for p in procs
93 ]
94 lctrl.set_string_items(items = items)
95 lctrl.set_data(data = procs)
96
97 gmListWidgets.get_choices_from_list (
98 parent = parent,
99 msg = _('\nSelect the procedure you want to edit !\n'),
100 caption = _('Editing performed procedures ...'),
101 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
102 single_selection = True,
103 edit_callback = edit,
104 new_callback = edit,
105 delete_callback = delete,
106 refresh_callback = refresh
107 )
108
120
121 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
122
123 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
124
133
135 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
136 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
137 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
138 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
139 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
140
141
142 mp = gmMatchProvider.cMatchProvider_SQL2 (
143 queries = [
144 u"""
145 SELECT DISTINCT ON (data) data, location
146 FROM (
147 SELECT
148 clin_where as data,
149 clin_where as location
150 FROM
151 clin.procedure
152 WHERE
153 clin_where %(fragment_condition)s
154
155 UNION ALL
156
157 SELECT
158 narrative as data,
159 narrative as location
160 FROM
161 clin.hospital_stay
162 WHERE
163 narrative %(fragment_condition)s
164 ) as union_result
165 ORDER BY data
166 LIMIT 25"""
167 ]
168 )
169 mp.setThresholds(2, 4, 6)
170 self._PRW_location.matcher = mp
171
172
173 mp = gmMatchProvider.cMatchProvider_SQL2 (
174 queries = [
175 u"""
176 select distinct on (narrative) narrative, narrative
177 from clin.procedure
178 where narrative %(fragment_condition)s
179 order by narrative
180 limit 25
181 """ ]
182 )
183 mp.setThresholds(2, 4, 6)
184 self._PRW_procedure.matcher = mp
185
187 stay = self._PRW_hospital_stay.GetData()
188 if stay is None:
189 self._PRW_hospital_stay.SetText()
190 self._PRW_location.Enable(True)
191 self._PRW_episode.Enable(True)
192 self._LBL_hospital_details.SetLabel(u'')
193 else:
194 self._PRW_location.SetText()
195 self._PRW_location.Enable(False)
196 self._PRW_episode.SetText()
197 self._PRW_episode.Enable(False)
198 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
199
201 if self._PRW_location.GetValue().strip() == u'':
202 self._PRW_hospital_stay.Enable(True)
203
204 else:
205 self._PRW_hospital_stay.SetText()
206 self._PRW_hospital_stay.Enable(False)
207 self._PRW_hospital_stay.display_as_valid(True)
208
209
221
244
245
246
304
339
341 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
342
343 if self._DPRW_end.GetData() is None:
344 self.data['clin_end'] = None
345 else:
346 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
347
348 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
349
350 if self._PRW_hospital_stay.GetData() is None:
351 self.data['pk_hospital_stay'] = None
352 self.data['clin_where'] = self._PRW_location.GetValue().strip()
353 self.data['pk_episode'] = self._PRW_episode.GetData()
354 else:
355 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
356 self.data['clin_where'] = None
357 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
358 self.data['pk_episode'] = stay['pk_episode']
359
360 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
361
362 self.data.save()
363 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
364
365 return True
366
368 self._DPRW_date.SetText()
369 self._DPRW_end.SetText()
370 self._CHBOX_ongoing.SetValue(False)
371 self._CHBOX_ongoing.Enable(True)
372 self._PRW_hospital_stay.SetText()
373 self._PRW_location.SetText()
374 self._PRW_episode.SetText()
375 self._PRW_procedure.SetText()
376 self._PRW_codes.SetText()
377
378 self._PRW_procedure.SetFocus()
379
410
422
423
424
429
445
446
447
458
459 def delete(stay=None):
460 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
461 return True
462 gmDispatcher.send (
463 signal = u'statustext',
464 msg = _('Cannot delete hospitalization.'),
465 beep = True
466 )
467 return False
468
469 def refresh(lctrl):
470 stays = emr.get_hospital_stays()
471 items = [
472 [
473 s['admission'].strftime('%Y-%m-%d'),
474 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
475 s['episode'],
476 gmTools.coalesce(s['hospital'], u'')
477 ] for s in stays
478 ]
479 lctrl.set_string_items(items = items)
480 lctrl.set_data(data = stays)
481
482 gmListWidgets.get_choices_from_list (
483 parent = parent,
484 msg = _("The patient's hospitalizations:\n"),
485 caption = _('Editing hospitalizations ...'),
486 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
487 single_selection = True,
488 edit_callback = edit,
489 new_callback = edit,
490 delete_callback = delete,
491 refresh_callback = refresh
492 )
493
494
506
508 """Phrasewheel to allow selection of a hospitalization."""
510
511 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
512
513 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
514
515 mp = gmMatchProvider.cMatchProvider_SQL2 (
516 queries = [
517 u"""
518 select
519 pk_hospital_stay,
520 descr
521 from (
522 select distinct on (pk_hospital_stay)
523 pk_hospital_stay,
524 descr
525 from
526 (select
527 pk_hospital_stay,
528 (
529 to_char(admission, 'YYYY-Mon-DD')
530 || coalesce((' (' || hospital || '):'), ': ')
531 || episode
532 || coalesce((' (' || health_issue || ')'), '')
533 ) as descr
534 from
535 clin.v_pat_hospital_stays
536 where
537 %(ctxt_pat)s
538
539 hospital %(fragment_condition)s
540 or
541 episode %(fragment_condition)s
542 or
543 health_issue %(fragment_condition)s
544 ) as the_stays
545 ) as distinct_stays
546 order by descr
547 limit 25
548 """ ],
549 context = ctxt
550 )
551 mp.setThresholds(3, 4, 6)
552 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
553
554 self.matcher = mp
555 self.selection_only = True
556
557 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
558
559 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
560
564
565
566
568
569 valid = True
570
571 if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
572 valid = False
573 gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospitalization.'), beep = True)
574
575 if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
576 if self._PRW_discharge.date is not None:
577 if not self._PRW_discharge.date > self._PRW_admission.date:
578 valid = False
579 self._PRW_discharge.display_as_valid(False)
580 gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospitalization.'), beep = True)
581
582 if self._PRW_episode.GetValue().strip() == u'':
583 valid = False
584 self._PRW_episode.display_as_valid(False)
585 gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospitalization.'), beep = True)
586
587 return (valid is True)
588
601
611
617
627
629 print "this was not expected to be used in this edit area"
630
631
632
641
642 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
643
645 if parent is None:
646 parent = wx.GetApp().GetTopWindow()
647
648
649 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
650 if dlg.ShowModal() == wx.ID_OK:
651 dlg.Destroy()
652 return True
653 dlg.Destroy()
654 return False
655
658
659 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
660
661 if patient is None:
662 patient = gmPerson.gmCurrentPatient()
663
664 if not patient.connected:
665 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
666 return False
667
668 if parent is None:
669 parent = wx.GetApp().GetTopWindow()
670
671 emr = patient.get_emr()
672
673
674 def refresh(lctrl):
675 if encounters is None:
676 encs = emr.get_encounters()
677 else:
678 encs = encounters
679
680 items = [
681 [
682 e['started'].strftime('%x %H:%M'),
683 e['last_affirmed'].strftime('%H:%M'),
684 e['l10n_type'],
685 gmTools.coalesce(e['reason_for_encounter'], u''),
686 gmTools.coalesce(e['assessment_of_encounter'], u''),
687 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
688 e['pk_encounter']
689 ] for e in encs
690 ]
691 lctrl.set_string_items(items = items)
692 lctrl.set_data(data = encs)
693 active_pk = emr.active_encounter['pk_encounter']
694 for idx in range(len(encs)):
695 e = encs[idx]
696 if e['pk_encounter'] == active_pk:
697 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
698
699 def new():
700 cfg_db = gmCfg.cCfgSQL()
701
702 enc_type = cfg_db.get2 (
703 option = u'encounter.default_type',
704 workplace = gmSurgery.gmCurrentPractice().active_workplace,
705 bias = u'user',
706 default = u'in surgery'
707 )
708 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
709 return edit_encounter(parent = parent, encounter = enc)
710
711 def edit(enc=None):
712 return edit_encounter(parent = parent, encounter = enc)
713
714 def edit_active(enc=None):
715 return edit_encounter(parent = parent, encounter = emr.active_encounter)
716
717 def start_new(enc=None):
718 start_new_encounter(emr = emr)
719 return True
720
721 return gmListWidgets.get_choices_from_list (
722 parent = parent,
723 msg = _("The patient's encounters.\n"),
724 caption = _('Encounters ...'),
725 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
726 can_return_empty = False,
727 single_selection = single_selection,
728 refresh_callback = refresh,
729 edit_callback = edit,
730 new_callback = new,
731 ignore_OK_button = ignore_OK_button,
732 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
733 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
734 )
735
737 """This is used as the callback when the EMR detects that the
738 patient was here rather recently and wants to ask the
739 provider whether to continue the recent encounter.
740 """
741 if parent is None:
742 parent = wx.GetApp().GetTopWindow()
743
744 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
745 parent = None,
746 id = -1,
747 caption = caption,
748 question = msg,
749 button_defs = [
750 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
751 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
752 ],
753 show_checkbox = False
754 )
755
756 result = dlg.ShowModal()
757 dlg.Destroy()
758
759 if result == wx.ID_YES:
760 return True
761
762 return False
763
765
766 if parent is None:
767 parent = wx.GetApp().GetTopWindow()
768
769
770 def edit(enc_type=None):
771 return edit_encounter_type(parent = parent, encounter_type = enc_type)
772
773 def delete(enc_type=None):
774 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
775 return True
776 gmDispatcher.send (
777 signal = u'statustext',
778 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
779 beep = True
780 )
781 return False
782
783 def refresh(lctrl):
784 enc_types = gmEMRStructItems.get_encounter_types()
785 lctrl.set_string_items(items = enc_types)
786
787 gmListWidgets.get_choices_from_list (
788 parent = parent,
789 msg = _('\nSelect the encounter type you want to edit !\n'),
790 caption = _('Managing encounter types ...'),
791 columns = [_('Local name'), _('Encounter type')],
792 single_selection = True,
793 edit_callback = edit,
794 new_callback = edit,
795 delete_callback = delete,
796 refresh_callback = refresh
797 )
798
808
810
812 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
813
814 cmd = u"""
815 SELECT -- DISTINCT ON (data)
816 pk_encounter
817 AS data,
818 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type
819 AS list_label,
820 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
821 AS field_label
822 FROM
823 clin.v_pat_encounters
824 WHERE
825 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s
826 OR
827 l10n_type %(fragment_condition)s
828 OR
829 type %(fragment_condition)s
830 %(ctxt_patient)s
831 ORDER BY
832 list_label
833 LIMIT
834 30
835 """
836 context = {'ctxt_patient': {
837 'where_part': u'AND pk_patient = %(patient)s',
838 'placeholder': u'patient'
839 }}
840
841 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context)
842 self.matcher._SQL_data2match = u"""
843 SELECT
844 pk_encounter
845 AS data,
846 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type
847 AS list_label,
848 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type
849 AS field_label
850 FROM
851 clin.v_pat_encounters
852 WHERE
853 pk_encounter = %(pk)s
854 """
855 self.matcher.setThresholds(1, 3, 5)
856 self.selection_only = True
857
858 self.set_context(context = 'patient', val = None)
859
866
877
879 """Phrasewheel to allow selection of encounter type.
880
881 - user input interpreted as encounter type in English or local language
882 - data returned is pk of corresponding encounter type or None
883 """
885
886 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
887
888 mp = gmMatchProvider.cMatchProvider_SQL2 (
889 queries = [
890 u"""
891 SELECT
892 data,
893 field_label,
894 list_label
895 FROM (
896 SELECT DISTINCT ON (data) *
897 FROM (
898 SELECT
899 pk AS data,
900 _(description) AS field_label,
901 case
902 when _(description) = description then _(description)
903 else _(description) || ' (' || description || ')'
904 end AS list_label
905 FROM
906 clin.encounter_type
907 WHERE
908 _(description) %(fragment_condition)s
909 OR
910 description %(fragment_condition)s
911 ) AS q_distinct_pk
912 ) AS q_ordered
913 ORDER BY
914 list_label
915 """ ]
916 )
917 mp.setThresholds(2, 4, 6)
918
919 self.matcher = mp
920 self.selection_only = True
921 self.picklist_delay = 50
922
923 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
924
926
931
932
933
934
935
965
978
988
990 self._TCTRL_l10n_name.SetValue(u'')
991 self._TCTRL_name.SetValue(u'')
992 self._TCTRL_name.Enable(True)
993
995 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
996 self._TCTRL_name.SetValue(self.data['description'])
997
998 self._TCTRL_name.Enable(False)
999
1001 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
1002 self._TCTRL_name.SetValue(self.data['description'])
1003 self._TCTRL_name.Enable(True)
1004
1005
1006
1007
1008
1009
1010 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
1011
1013
1015 try:
1016 self.__encounter = kwargs['encounter']
1017 del kwargs['encounter']
1018 except KeyError:
1019 self.__encounter = None
1020
1021 try:
1022 msg = kwargs['msg']
1023 del kwargs['msg']
1024 except KeyError:
1025 msg = None
1026
1027 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
1028
1029 self.refresh(msg = msg)
1030
1031
1032
1033 - def refresh(self, encounter=None, msg=None):
1034
1035 if msg is not None:
1036 self._LBL_instructions.SetLabel(msg)
1037
1038 if encounter is not None:
1039 self.__encounter = encounter
1040
1041 if self.__encounter is None:
1042 return True
1043
1044
1045
1046 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
1047 self._LBL_patient.SetLabel(pat.get_description_gender())
1048
1049 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
1050
1051 fts = gmDateTime.cFuzzyTimestamp (
1052 timestamp = self.__encounter['started'],
1053 accuracy = gmDateTime.acc_minutes
1054 )
1055 self._PRW_start.SetText(fts.format_accurately(), data=fts)
1056
1057 fts = gmDateTime.cFuzzyTimestamp (
1058 timestamp = self.__encounter['last_affirmed'],
1059 accuracy = gmDateTime.acc_minutes
1060 )
1061 self._PRW_end.SetText(fts.format_accurately(), data=fts)
1062
1063
1064 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
1065 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
1066 self._PRW_rfe_codes.SetText(val, data)
1067
1068
1069 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
1070 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
1071 self._PRW_aoe_codes.SetText(val, data)
1072
1073
1074 if self.__encounter['last_affirmed'] == self.__encounter['started']:
1075 self._PRW_end.SetFocus()
1076 else:
1077 self._TCTRL_aoe.SetFocus()
1078
1079 return True
1080
1082
1083 if self._PRW_encounter_type.GetData() is None:
1084 self._PRW_encounter_type.SetBackgroundColour('pink')
1085 self._PRW_encounter_type.Refresh()
1086 self._PRW_encounter_type.SetFocus()
1087 return False
1088 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1089 self._PRW_encounter_type.Refresh()
1090
1091
1092 if self._PRW_start.GetValue().strip() == u'':
1093 self._PRW_start.SetBackgroundColour('pink')
1094 self._PRW_start.Refresh()
1095 self._PRW_start.SetFocus()
1096 return False
1097 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False):
1098 self._PRW_start.SetBackgroundColour('pink')
1099 self._PRW_start.Refresh()
1100 self._PRW_start.SetFocus()
1101 return False
1102 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1103 self._PRW_start.Refresh()
1104
1105
1106
1107
1108
1109
1110
1111 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False):
1112 self._PRW_end.SetBackgroundColour('pink')
1113 self._PRW_end.Refresh()
1114 self._PRW_end.SetFocus()
1115 return False
1116 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1117 self._PRW_end.Refresh()
1118
1119 return True
1120
1122 if not self.__is_valid_for_save():
1123 return False
1124
1125 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1126 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1127 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1128 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1129 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1130 self.__encounter.save_payload()
1131
1132 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1133 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1134
1135 return True
1136
1137
1139
1141 encounter = kwargs['encounter']
1142 del kwargs['encounter']
1143
1144 try:
1145 button_defs = kwargs['button_defs']
1146 del kwargs['button_defs']
1147 except KeyError:
1148 button_defs = None
1149
1150 try:
1151 msg = kwargs['msg']
1152 del kwargs['msg']
1153 except KeyError:
1154 msg = None
1155
1156 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1157 self.SetSize((450, 280))
1158 self.SetMinSize((450, 280))
1159
1160 if button_defs is not None:
1161 self._BTN_save.SetLabel(button_defs[0][0])
1162 self._BTN_save.SetToolTipString(button_defs[0][1])
1163 self._BTN_close.SetLabel(button_defs[1][0])
1164 self._BTN_close.SetToolTipString(button_defs[1][1])
1165 self.Refresh()
1166
1167 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1168
1169 self.Fit()
1170
1177
1178 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
1179
1181
1186
1188 self._TCTRL_encounter.SetValue(u'')
1189 self._TCTRL_encounter.SetToolTipString(u'')
1190 self._BTN_new.Enable(False)
1191 self._BTN_list.Enable(False)
1192
1194 pat = gmPerson.gmCurrentPatient()
1195 if not pat.connected:
1196 self.clear()
1197 return
1198
1199 enc = pat.get_emr().active_encounter
1200 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
1201 self._TCTRL_encounter.SetToolTipString (
1202 _('The active encounter of the current patient:\n\n%s') %
1203 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
1204 )
1205 self._BTN_new.Enable(True)
1206 self._BTN_list.Enable(True)
1207
1209 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
1210
1211 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
1212
1213
1214 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
1215 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
1216 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1217
1218
1219
1221 wx.CallAfter(self.clear)
1222
1224 wx.CallAfter(self.refresh)
1225 return True
1226
1230
1234
1237
1238
1239
1249
1319
1321 """Prepare changing health issue for an episode.
1322
1323 Checks for two-open-episodes conflict. When this
1324 function succeeds, the pk_health_issue has been set
1325 on the episode instance and the episode should - for
1326 all practical purposes - be ready for save_payload().
1327 """
1328
1329 if not episode['episode_open']:
1330 episode['pk_health_issue'] = target_issue['pk_health_issue']
1331 if save_to_backend:
1332 episode.save_payload()
1333 return True
1334
1335
1336 if target_issue is None:
1337 episode['pk_health_issue'] = None
1338 if save_to_backend:
1339 episode.save_payload()
1340 return True
1341
1342
1343 db_cfg = gmCfg.cCfgSQL()
1344 epi_ttl = int(db_cfg.get2 (
1345 option = u'episode.ttl',
1346 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1347 bias = 'user',
1348 default = 60
1349 ))
1350 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1351 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1352 existing_epi = target_issue.get_open_episode()
1353
1354
1355 if existing_epi is None:
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 existing_epi['pk_episode'] == episode['pk_episode']:
1363 episode['pk_health_issue'] = target_issue['pk_health_issue']
1364 if save_to_backend:
1365 episode.save_payload()
1366 return True
1367
1368
1369 move_range = episode.get_access_range()
1370 exist_range = existing_epi.get_access_range()
1371 question = _(
1372 'You want to associate the running episode:\n\n'
1373 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1374 'with the health issue:\n\n'
1375 ' "%(issue_name)s"\n\n'
1376 'There already is another episode running\n'
1377 'for this health issue:\n\n'
1378 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1379 'However, there can only be one running\n'
1380 'episode per health issue.\n\n'
1381 'Which episode do you want to close ?'
1382 ) % {
1383 'new_epi_name': episode['description'],
1384 'new_epi_start': move_range[0].strftime('%m/%y'),
1385 'new_epi_end': move_range[1].strftime('%m/%y'),
1386 'issue_name': target_issue['description'],
1387 'old_epi_name': existing_epi['description'],
1388 'old_epi_start': exist_range[0].strftime('%m/%y'),
1389 'old_epi_end': exist_range[1].strftime('%m/%y')
1390 }
1391 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1392 parent = None,
1393 id = -1,
1394 caption = _('Resolving two-running-episodes conflict'),
1395 question = question,
1396 button_defs = [
1397 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1398 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1399 ]
1400 )
1401 decision = dlg.ShowModal()
1402
1403 if decision == wx.ID_CANCEL:
1404
1405 return False
1406
1407 elif decision == wx.ID_YES:
1408
1409 existing_epi['episode_open'] = False
1410 existing_epi.save_payload()
1411
1412 elif decision == wx.ID_NO:
1413
1414 episode['episode_open'] = False
1415
1416 else:
1417 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1418
1419 episode['pk_health_issue'] = target_issue['pk_health_issue']
1420 if save_to_backend:
1421 episode.save_payload()
1422 return True
1423
1447
1449 """Let user select an episode *description*.
1450
1451 The user can select an episode description from the previously
1452 used descriptions across all episodes across all patients.
1453
1454 Selection is done with a phrasewheel so the user can
1455 type the episode name and matches will be shown. Typing
1456 "*" will show the entire list of episodes.
1457
1458 If the user types a description not existing yet a
1459 new episode description will be returned.
1460 """
1462
1463 mp = gmMatchProvider.cMatchProvider_SQL2 (
1464 queries = [
1465 u"""
1466 SELECT DISTINCT ON (description)
1467 description
1468 AS data,
1469 description
1470 AS field_label,
1471 description || ' ('
1472 || CASE
1473 WHEN is_open IS TRUE THEN _('ongoing')
1474 ELSE _('closed')
1475 END
1476 || ')'
1477 AS list_label
1478 FROM
1479 clin.episode
1480 WHERE
1481 description %(fragment_condition)s
1482 ORDER BY description
1483 LIMIT 30
1484 """
1485 ]
1486 )
1487 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1488 self.matcher = mp
1489
1491 """Let user select an episode.
1492
1493 The user can select an episode from the existing episodes of a
1494 patient. Selection is done with a phrasewheel so the user
1495 can type the episode name and matches will be shown. Typing
1496 "*" will show the entire list of episodes. Closed episodes
1497 will be marked as such. If the user types an episode name not
1498 in the list of existing episodes a new episode can be created
1499 from it if the programmer activated that feature.
1500
1501 If keyword <patient_id> is set to None or left out the control
1502 will listen to patient change signals and therefore act on
1503 gmPerson.gmCurrentPatient() changes.
1504 """
1506
1507 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1508
1509 mp = gmMatchProvider.cMatchProvider_SQL2 (
1510 queries = [
1511 u"""(
1512
1513 select
1514 pk_episode
1515 as data,
1516 description
1517 as field_label,
1518 coalesce (
1519 description || ' - ' || health_issue,
1520 description
1521 ) as list_label,
1522 1 as rank
1523 from
1524 clin.v_pat_episodes
1525 where
1526 episode_open is true and
1527 description %(fragment_condition)s
1528 %(ctxt_pat)s
1529
1530 ) union all (
1531
1532 select
1533 pk_episode
1534 as data,
1535 description
1536 as field_label,
1537 coalesce (
1538 description || _(' (closed)') || ' - ' || health_issue,
1539 description || _(' (closed)')
1540 ) as list_label,
1541 2 as rank
1542 from
1543 clin.v_pat_episodes
1544 where
1545 description %(fragment_condition)s and
1546 episode_open is false
1547 %(ctxt_pat)s
1548
1549 )
1550
1551 order by rank, list_label
1552 limit 30"""
1553 ],
1554 context = ctxt
1555 )
1556
1557 try:
1558 kwargs['patient_id']
1559 except KeyError:
1560 kwargs['patient_id'] = None
1561
1562 if kwargs['patient_id'] is None:
1563 self.use_current_patient = True
1564 self.__register_patient_change_signals()
1565 pat = gmPerson.gmCurrentPatient()
1566 if pat.connected:
1567 mp.set_context('pat', pat.ID)
1568 else:
1569 self.use_current_patient = False
1570 self.__patient_id = int(kwargs['patient_id'])
1571 mp.set_context('pat', self.__patient_id)
1572
1573 del kwargs['patient_id']
1574
1575 gmPhraseWheel.cPhraseWheel.__init__ (
1576 self,
1577 *args,
1578 **kwargs
1579 )
1580 self.matcher = mp
1581
1582
1583
1585 if self.use_current_patient:
1586 return False
1587 self.__patient_id = int(patient_id)
1588 self.set_context('pat', self.__patient_id)
1589 return True
1590
1591 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1594
1596
1597 epi_name = self.GetValue().strip()
1598 if epi_name == u'':
1599 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1600 _log.debug('cannot create episode without name')
1601 return
1602
1603 if self.use_current_patient:
1604 pat = gmPerson.gmCurrentPatient()
1605 else:
1606 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1607
1608 emr = pat.get_emr()
1609 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1610 if epi is None:
1611 self.data = {}
1612 else:
1613 self.SetText (
1614 value = epi_name,
1615 data = epi['pk_episode']
1616 )
1617
1620
1621
1622
1626
1629
1631 if self.use_current_patient:
1632 patient = gmPerson.gmCurrentPatient()
1633 self.set_context('pat', patient.ID)
1634 return True
1635
1636 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1637
1638 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1639
1652
1653
1654
1656
1657 errors = False
1658
1659 if len(self._PRW_description.GetValue().strip()) == 0:
1660 errors = True
1661 self._PRW_description.display_as_valid(False)
1662 self._PRW_description.SetFocus()
1663 else:
1664 self._PRW_description.display_as_valid(True)
1665 self._PRW_description.Refresh()
1666
1667 return not errors
1668
1670
1671 pat = gmPerson.gmCurrentPatient()
1672 emr = pat.get_emr()
1673
1674 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1675 epi['summary'] = self._TCTRL_status.GetValue().strip()
1676 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1677 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1678
1679 issue_name = self._PRW_issue.GetValue().strip()
1680 if len(issue_name) != 0:
1681 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1682 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1683
1684 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1685 gmDispatcher.send (
1686 signal = 'statustext',
1687 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1688 epi['description'],
1689 issue['description']
1690 )
1691 )
1692 gmEMRStructItems.delete_episode(episode = epi)
1693 return False
1694
1695 epi.save()
1696
1697 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1698
1699 self.data = epi
1700 return True
1701
1703
1704 self.data['description'] = self._PRW_description.GetValue().strip()
1705 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1706 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1707 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1708
1709 issue_name = self._PRW_issue.GetValue().strip()
1710 if len(issue_name) == 0:
1711 self.data['pk_health_issue'] = None
1712 else:
1713 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1714 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1715
1716 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1717 gmDispatcher.send (
1718 signal = 'statustext',
1719 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1720 self.data['description'],
1721 issue['description']
1722 )
1723 )
1724 return False
1725
1726 self.data.save()
1727 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1728
1729 return True
1730
1743
1762
1764 self._refresh_as_new()
1765
1766
1767
1777
1779
1780 if parent is None:
1781 parent = wx.GetApp().GetTopWindow()
1782
1783 def refresh(lctrl):
1784 issues = emr.get_health_issues()
1785 items = [
1786 [
1787 gmTools.bool2subst(i['is_confidential'], _('CONFIDENTIAL'), u'', u''),
1788 i['description'],
1789 gmTools.bool2subst(i['clinically_relevant'], _('relevant'), u'', u''),
1790 gmTools.bool2subst(i['is_active'], _('active'), u'', u''),
1791 gmTools.bool2subst(i['is_cause_of_death'], _('fatal'), u'', u'')
1792 ] for i in issues
1793 ]
1794 lctrl.set_string_items(items = items)
1795 lctrl.set_data(data = issues)
1796
1797 return gmListWidgets.get_choices_from_list (
1798 parent = parent,
1799 msg = _('\nSelect the health issues !\n'),
1800 caption = _('Showing health issues ...'),
1801 columns = [u'', _('Health issue'), u'', u'', u''],
1802 single_selection = False,
1803
1804
1805
1806 refresh_callback = refresh
1807 )
1808
1810
1811
1812
1814
1815 issues = kwargs['issues']
1816 del kwargs['issues']
1817
1818 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1819
1820 self.SetTitle(_('Select the health issues you are interested in ...'))
1821 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1822
1823 for issue in issues:
1824 if issue['is_confidential']:
1825 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1826 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1827 else:
1828 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1829
1830 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1831 if issue['clinically_relevant']:
1832 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1833 if issue['is_active']:
1834 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1835 if issue['is_cause_of_death']:
1836 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1837
1838 self._LCTRL_items.set_column_widths()
1839 self._LCTRL_items.set_data(data = issues)
1840
1842 """Let the user select a health issue.
1843
1844 The user can select a health issue from the existing issues
1845 of a patient. Selection is done with a phrasewheel so the user
1846 can type the issue name and matches will be shown. Typing
1847 "*" will show the entire list of issues. Inactive issues
1848 will be marked as such. If the user types an issue name not
1849 in the list of existing issues a new issue can be created
1850 from it if the programmer activated that feature.
1851
1852 If keyword <patient_id> is set to None or left out the control
1853 will listen to patient change signals and therefore act on
1854 gmPerson.gmCurrentPatient() changes.
1855 """
1857
1858 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1859
1860 mp = gmMatchProvider.cMatchProvider_SQL2 (
1861
1862 queries = [
1863 u"""
1864 SELECT
1865 data,
1866 field_label,
1867 list_label
1868 FROM ((
1869 SELECT
1870 pk_health_issue AS data,
1871 description AS field_label,
1872 description AS list_label
1873 FROM clin.v_health_issues
1874 WHERE
1875 is_active IS true
1876 AND
1877 description %(fragment_condition)s
1878 AND
1879 %(ctxt_pat)s
1880
1881 ) UNION (
1882
1883 SELECT
1884 pk_health_issue AS data,
1885 description AS field_label,
1886 description || _(' (inactive)') AS list_label
1887 FROM clin.v_health_issues
1888 WHERE
1889 is_active IS false
1890 AND
1891 description %(fragment_condition)s
1892 AND
1893 %(ctxt_pat)s
1894 )) AS union_query
1895 ORDER BY
1896 list_label"""],
1897 context = ctxt
1898 )
1899
1900 try: kwargs['patient_id']
1901 except KeyError: kwargs['patient_id'] = None
1902
1903 if kwargs['patient_id'] is None:
1904 self.use_current_patient = True
1905 self.__register_patient_change_signals()
1906 pat = gmPerson.gmCurrentPatient()
1907 if pat.connected:
1908 mp.set_context('pat', pat.ID)
1909 else:
1910 self.use_current_patient = False
1911 self.__patient_id = int(kwargs['patient_id'])
1912 mp.set_context('pat', self.__patient_id)
1913
1914 del kwargs['patient_id']
1915
1916 gmPhraseWheel.cPhraseWheel.__init__ (
1917 self,
1918 *args,
1919 **kwargs
1920 )
1921 self.matcher = mp
1922
1923
1924
1926 if self.use_current_patient:
1927 return False
1928 self.__patient_id = int(patient_id)
1929 self.set_context('pat', self.__patient_id)
1930 return True
1931
1933 issue_name = self.GetValue().strip()
1934 if issue_name == u'':
1935 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create health issue without name.'), beep = True)
1936 _log.debug('cannot create health issue without name')
1937 return
1938
1939 if self.use_current_patient:
1940 pat = gmPerson.gmCurrentPatient()
1941 else:
1942 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1943
1944 emr = pat.get_emr()
1945 issue = emr.add_health_issue(issue_name = issue_name)
1946
1947 if issue is None:
1948 self.data = {}
1949 else:
1950 self.SetText (
1951 value = issue_name,
1952 data = issue['pk_health_issue']
1953 )
1954
1957
1958
1959
1963
1966
1968 if self.use_current_patient:
1969 patient = gmPerson.gmCurrentPatient()
1970 self.set_context('pat', patient.ID)
1971 return True
1972
1973 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg
1974
1976
1978 try:
1979 msg = kwargs['message']
1980 except KeyError:
1981 msg = None
1982 del kwargs['message']
1983 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1984 if msg is not None:
1985 self._lbl_message.SetLabel(label=msg)
1986
1997
1998 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1999
2000 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
2001 """Panel encapsulating health issue edit area functionality."""
2002
2004
2005 try:
2006 issue = kwargs['issue']
2007 except KeyError:
2008 issue = None
2009
2010 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
2011
2012 gmEditArea.cGenericEditAreaMixin.__init__(self)
2013
2014
2015 mp = gmMatchProvider.cMatchProvider_SQL2 (
2016 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
2017 )
2018 mp.setThresholds(1, 3, 5)
2019 self._PRW_condition.matcher = mp
2020
2021 mp = gmMatchProvider.cMatchProvider_SQL2 (
2022 queries = [u"""
2023 select distinct on (grouping) grouping, grouping from (
2024
2025 select rank, grouping from ((
2026
2027 select
2028 grouping,
2029 1 as rank
2030 from
2031 clin.health_issue
2032 where
2033 grouping %%(fragment_condition)s
2034 and
2035 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
2036
2037 ) union (
2038
2039 select
2040 grouping,
2041 2 as rank
2042 from
2043 clin.health_issue
2044 where
2045 grouping %%(fragment_condition)s
2046
2047 )) as union_result
2048
2049 order by rank
2050
2051 ) as order_result
2052
2053 limit 50""" % gmPerson.gmCurrentPatient().ID
2054 ]
2055 )
2056 mp.setThresholds(1, 3, 5)
2057 self._PRW_grouping.matcher = mp
2058
2059 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
2060 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
2061
2062 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
2063 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
2064
2065 self._PRW_year_noted.Enable(True)
2066
2067 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
2068
2069 self.data = issue
2070
2071
2072
2092
2094 pat = gmPerson.gmCurrentPatient()
2095 emr = pat.get_emr()
2096
2097 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
2098
2099 side = u''
2100 if self._ChBOX_left.GetValue():
2101 side += u's'
2102 if self._ChBOX_right.GetValue():
2103 side += u'd'
2104 issue['laterality'] = side
2105
2106 issue['summary'] = self._TCTRL_status.GetValue().strip()
2107 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2108 issue['grouping'] = self._PRW_grouping.GetValue().strip()
2109 issue['is_active'] = self._ChBOX_active.GetValue()
2110 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
2111 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
2112 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
2113
2114 age_noted = self._PRW_age_noted.GetData()
2115 if age_noted is not None:
2116 issue['age_noted'] = age_noted
2117
2118 issue.save()
2119
2120 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2121
2122 self.data = issue
2123 return True
2124
2126
2127 self.data['description'] = self._PRW_condition.GetValue().strip()
2128
2129 side = u''
2130 if self._ChBOX_left.GetValue():
2131 side += u's'
2132 if self._ChBOX_right.GetValue():
2133 side += u'd'
2134 self.data['laterality'] = side
2135
2136 self.data['summary'] = self._TCTRL_status.GetValue().strip()
2137 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2138 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
2139 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
2140 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
2141 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
2142 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
2143
2144 age_noted = self._PRW_age_noted.GetData()
2145 if age_noted is not None:
2146 self.data['age_noted'] = age_noted
2147
2148 self.data.save()
2149 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2150
2151 return True
2152
2154 self._PRW_condition.SetText()
2155 self._ChBOX_left.SetValue(0)
2156 self._ChBOX_right.SetValue(0)
2157 self._PRW_codes.SetText()
2158 self._on_leave_codes()
2159 self._PRW_certainty.SetText()
2160 self._PRW_grouping.SetText()
2161 self._TCTRL_status.SetValue(u'')
2162 self._PRW_age_noted.SetText()
2163 self._PRW_year_noted.SetText()
2164 self._ChBOX_active.SetValue(0)
2165 self._ChBOX_relevant.SetValue(1)
2166 self._ChBOX_confidential.SetValue(0)
2167 self._ChBOX_caused_death.SetValue(0)
2168
2169 return True
2170
2211
2213 return self._refresh_as_new()
2214
2215
2216
2218 if not self._PRW_codes.IsModified():
2219 return True
2220
2221 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2222
2224
2225 if not self._PRW_age_noted.IsModified():
2226 return True
2227
2228 str_age = self._PRW_age_noted.GetValue().strip()
2229
2230 if str_age == u'':
2231 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2232 return True
2233
2234 age = gmDateTime.str2interval(str_interval = str_age)
2235
2236 if age is None:
2237 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
2238 self._PRW_age_noted.SetBackgroundColour('pink')
2239 self._PRW_age_noted.Refresh()
2240 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2241 return True
2242
2243 pat = gmPerson.gmCurrentPatient()
2244 if pat['dob'] is not None:
2245 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2246
2247 if age >= max_age:
2248 gmDispatcher.send (
2249 signal = 'statustext',
2250 msg = _(
2251 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2252 ) % (age, pat.get_medical_age())
2253 )
2254 self._PRW_age_noted.SetBackgroundColour('pink')
2255 self._PRW_age_noted.Refresh()
2256 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2257 return True
2258
2259 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2260 self._PRW_age_noted.Refresh()
2261 self._PRW_age_noted.SetData(data=age)
2262
2263 if pat['dob'] is not None:
2264 fts = gmDateTime.cFuzzyTimestamp (
2265 timestamp = pat['dob'] + age,
2266 accuracy = gmDateTime.acc_months
2267 )
2268 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2269
2270
2271
2272
2273
2274 return True
2275
2277
2278 if not self._PRW_year_noted.IsModified():
2279 return True
2280
2281 year_noted = self._PRW_year_noted.GetData()
2282
2283 if year_noted is None:
2284 if self._PRW_year_noted.GetValue().strip() == u'':
2285 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2286 return True
2287 self._PRW_year_noted.SetBackgroundColour('pink')
2288 self._PRW_year_noted.Refresh()
2289 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2290 return True
2291
2292 year_noted = year_noted.get_pydt()
2293
2294 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2295 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2296 self._PRW_year_noted.SetBackgroundColour('pink')
2297 self._PRW_year_noted.Refresh()
2298 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2299 return True
2300
2301 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2302 self._PRW_year_noted.Refresh()
2303
2304 pat = gmPerson.gmCurrentPatient()
2305 if pat['dob'] is not None:
2306 issue_age = year_noted - pat['dob']
2307 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2308 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2309
2310 return True
2311
2313 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2314 return True
2315
2317 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2318 return True
2319
2320
2321
2323
2325
2326 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2327
2328 self.selection_only = False
2329
2330 mp = gmMatchProvider.cMatchProvider_FixedList (
2331 aSeq = [
2332 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2333 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2334 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2335 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2336 ]
2337 )
2338 mp.setThresholds(1, 2, 4)
2339 self.matcher = mp
2340
2341 self.SetToolTipString(_(
2342 "The diagnostic classification or grading of this assessment.\n"
2343 "\n"
2344 "This documents how certain one is about this being a true diagnosis."
2345 ))
2346
2347
2348
2349 if __name__ == '__main__':
2350
2351
2353 """
2354 Test application for testing EMR struct widgets
2355 """
2356
2358 """
2359 Create test application UI
2360 """
2361 frame = wx.Frame (
2362 None,
2363 -4,
2364 'Testing EMR struct widgets',
2365 size=wx.Size(600, 400),
2366 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2367 )
2368 filemenu= wx.Menu()
2369 filemenu.AppendSeparator()
2370 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2371
2372
2373 menuBar = wx.MenuBar()
2374 menuBar.Append(filemenu,"&File")
2375
2376 frame.SetMenuBar(menuBar)
2377
2378 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2379 wx.DefaultPosition, wx.DefaultSize, 0 )
2380
2381
2382 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2383
2384
2385 self.__pat = gmPerson.gmCurrentPatient()
2386
2387 frame.Show(1)
2388 return 1
2389
2391 """
2392 Close test aplication
2393 """
2394 self.ExitMainLoop ()
2395
2405
2414
2415
2416
2417
2418
2426
2432
2434 app = wx.PyWidgetTester(size = (400, 40))
2435 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2436 app.MainLoop()
2437
2439 app = wx.PyWidgetTester(size = (400, 40))
2440 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2441
2442 app.MainLoop()
2443
2445 app = wx.PyWidgetTester(size = (200, 300))
2446 edit_health_issue(parent=app.frame, issue=None)
2447
2449 app = wx.PyWidgetTester(size = (200, 300))
2450 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2451 app.MainLoop()
2452
2454 app = wx.PyWidgetTester(size = (200, 300))
2455 edit_procedure(parent=app.frame)
2456
2457
2458 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2459
2460 gmI18N.activate_locale()
2461 gmI18N.install_domain()
2462 gmDateTime.init()
2463
2464
2465 pat = gmPersonSearch.ask_for_patient()
2466 if pat is None:
2467 print "No patient. Exiting gracefully..."
2468 sys.exit(0)
2469 gmPatSearchWidgets.set_active_patient(patient=pat)
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487 test_edit_procedure()
2488
2489
2490