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
11
12 __version__ = "$Revision: 1.114 $"
13 __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
14 __license__ = "GPL"
15
16
17 import sys, re, datetime as pydt, logging, time
18
19
20
21 import wx
22 import wx.lib.pubsub as wxps
23
24
25
26 if __name__ == '__main__':
27 sys.path.insert(0, '../../')
28 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
29 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery
30 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
31 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
32 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
33 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl, wxgEncounterEditAreaDlg
34 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
35 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
36
37
38 _log = logging.getLogger('gm.ui')
39 _log.info(__version__)
40
41
42
53
54 def delete(procedure=None):
55 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
56 return True
57
58 gmDispatcher.send (
59 signal = u'statustext',
60 msg = _('Cannot delete performed procedure.'),
61 beep = True
62 )
63 return False
64
65 def refresh(lctrl):
66 procs = emr.get_performed_procedures()
67
68 items = [
69 [
70 p['clin_when'].strftime('%Y-%m-%d'),
71 p['clin_where'],
72 p['episode'],
73 p['performed_procedure']
74 ] for p in procs
75 ]
76 lctrl.set_string_items(items = items)
77 lctrl.set_data(data = procs)
78
79 gmListWidgets.get_choices_from_list (
80 parent = parent,
81 msg = _('\nSelect the procedure you want to edit !\n'),
82 caption = _('Editing performed procedures ...'),
83 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
84 single_selection = True,
85 edit_callback = edit,
86 new_callback = edit,
87 delete_callback = delete,
88 refresh_callback = refresh
89 )
90
91 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
92
104
105 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
106
115
117 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
118 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
119
120
121 mp = gmMatchProvider.cMatchProvider_SQL2 (
122 queries = [
123 u"""
124 select distinct on (clin_where) clin_where, clin_where
125 from clin.procedure
126 where clin_where %(fragment_condition)s
127 order by clin_where
128 limit 25
129 """ ]
130 )
131 mp.setThresholds(2, 4, 6)
132 self._PRW_location.matcher = mp
133
134
135 mp = gmMatchProvider.cMatchProvider_SQL2 (
136 queries = [
137 u"""
138 select distinct on (narrative) narrative, narrative
139 from clin.procedure
140 where narrative %(fragment_condition)s
141 order by narrative
142 limit 25
143 """ ]
144 )
145 mp.setThresholds(2, 4, 6)
146 self._PRW_procedure.matcher = mp
147
149 if self._PRW_hospital_stay.GetData() is None:
150 self._PRW_hospital_stay.SetText()
151 self._PRW_episode.Enable(True)
152 else:
153 self._PRW_location.SetText()
154 self._PRW_episode.SetText()
155 self._PRW_episode.Enable(False)
156
158 if self._PRW_location.GetValue().strip() == u'':
159 return
160
161 self._PRW_hospital_stay.SetText()
162 self._PRW_episode.Enable(True)
163
164
165
167
168 has_errors = False
169
170 if not self._DPRW_date.is_valid_timestamp():
171 self._DPRW_date.display_as_valid(False)
172 has_errors = True
173 else:
174 self._DPRW_date.display_as_valid(True)
175
176 if self._PRW_hospital_stay.GetData() is None:
177 if self._PRW_episode.GetData() is None:
178 self._PRW_episode.display_as_valid(False)
179 has_errors = True
180 else:
181 self._PRW_episode.display_as_valid(True)
182 else:
183 self._PRW_episode.display_as_valid(True)
184
185 if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''):
186 self._PRW_procedure.display_as_valid(False)
187 has_errors = True
188 else:
189 self._PRW_procedure.display_as_valid(True)
190
191 invalid_location = (
192 (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'')
193 or
194 (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'')
195 )
196 if invalid_location:
197 self._PRW_hospital_stay.display_as_valid(False)
198 self._PRW_location.display_as_valid(False)
199 has_errors = True
200 else:
201 self._PRW_hospital_stay.display_as_valid(True)
202 self._PRW_location.display_as_valid(True)
203
204 wxps.Publisher().sendMessage (
205 topic = 'statustext',
206 data = {'msg': _('Cannot save procedure.'), 'beep': True}
207 )
208
209 return (has_errors is False)
210
234
236 self.data['clin_when'] = self._DPRW_date.data.get_pydt()
237
238 if self._PRW_hospital_stay.GetData() is None:
239 self.data['pk_hospital_stay'] = None
240 self.data['clin_where'] = self._PRW_location.GetValue().strip()
241 self.data['pk_episode'] = self._PRW_episode.GetData()
242 else:
243 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
244 self.data['clin_where'] = None
245 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
246 self.data['pk_episode'] = stay['pk_episode']
247
248 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
249
250 self.data.save()
251 return True
252
261
275
287
291
292
293
304
305 def delete(stay=None):
306 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
307 return True
308 gmDispatcher.send (
309 signal = u'statustext',
310 msg = _('Cannot delete hospital stay.'),
311 beep = True
312 )
313 return False
314
315 def refresh(lctrl):
316 stays = emr.get_hospital_stays()
317 items = [
318 [
319 s['admission'].strftime('%Y-%m-%d'),
320 gmTools.coalesce(s['discharge'], u''),
321 s['episode'],
322 gmTools.coalesce(s['hospital'], u'')
323 ] for s in stays
324 ]
325 lctrl.set_string_items(items = items)
326 lctrl.set_data(data = stays)
327
328 gmListWidgets.get_choices_from_list (
329 parent = parent,
330 msg = _('\nSelect the hospital stay you want to edit !\n'),
331 caption = _('Editing hospital stays ...'),
332 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
333 single_selection = True,
334 edit_callback = edit,
335 new_callback = edit,
336 delete_callback = delete,
337 refresh_callback = refresh
338 )
339
340
352
354 """Phrasewheel to allow selection of a hospital stay.
355 """
357
358 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
359
360 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
361
362 mp = gmMatchProvider.cMatchProvider_SQL2 (
363 queries = [
364 u"""
365 select
366 pk_hospital_stay,
367 descr
368 from (
369 select distinct on (pk_hospital_stay)
370 pk_hospital_stay,
371 descr
372 from
373 (select
374 pk_hospital_stay,
375 (
376 to_char(admission, 'YYYY-Mon-DD')
377 || coalesce((' (' || hospital || '):'), ': ')
378 || episode
379 || coalesce((' (' || health_issue || ')'), '')
380 ) as descr
381 from
382 clin.v_pat_hospital_stays
383 where
384 %(ctxt_pat)s
385
386 hospital %(fragment_condition)s
387 or
388 episode %(fragment_condition)s
389 or
390 health_issue %(fragment_condition)s
391 ) as the_stays
392 ) as distinct_stays
393 order by descr
394 limit 25
395 """ ],
396 context = ctxt
397 )
398 mp.setThresholds(3, 4, 6)
399 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
400
401 self.matcher = mp
402 self.selection_only = True
403
404 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
405
406 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
407
411
412
413
415 if not self._DP_admission.GetValue().IsValid():
416 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
417 wxps.Publisher().sendMessage (
418 topic = 'statustext',
419 data = {'msg': _('Missing admission data. Cannot save hospital stay.'), 'beep': True}
420 )
421 return False
422 else:
423 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
424
425 if self._DP_discharge.GetValue().IsValid():
426 if not self._DP_discharge.GetValue().IsLaterThan(self._DP_admission.GetValue()):
427 self._DP_discharge.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
428 wxps.Publisher().sendMessage (
429 topic = 'statustext',
430 data = {'msg': _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), 'beep': True}
431 )
432 return False
433
434 return True
435
453
464
469
470
482
484 print "this was not expected to be used in this edit area"
485
486
487
496
498
499 if parent is None:
500 parent = wx.GetApp().GetTopWindow()
501
502
503 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
504 dlg.ShowModal()
505
506 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None):
507
508 if patient is None:
509 patient = gmPerson.gmCurrentPatient()
510
511 if not patient.connected:
512 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
513 return False
514
515 if parent is None:
516 parent = wx.GetApp().GetTopWindow()
517
518 emr = patient.get_emr()
519
520
521 def refresh(lctrl):
522 if encounters is not None:
523 encs = encounters
524 else:
525 encs = emr.get_encounters()
526
527 items = [
528 [
529 e['started'].strftime('%x %H:%M'),
530 e['last_affirmed'].strftime('%H:%M'),
531 e['l10n_type'],
532 gmTools.coalesce(e['reason_for_encounter'], u''),
533 gmTools.coalesce(e['assessment_of_encounter'], u''),
534 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
535 e['pk_encounter']
536 ] for e in encs
537 ]
538
539 lctrl.set_string_items(items = items)
540 lctrl.set_data(data = encs)
541
542 def edit(enc = None):
543 return edit_encounter(parent = parent, encounter = enc)
544
545 return gmListWidgets.get_choices_from_list (
546 parent = parent,
547 msg = _('\nBelow find the relevant encounters of the patient.\n'),
548 caption = _('Encounters ...'),
549 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
550 can_return_empty = True,
551 single_selection = single_selection,
552 refresh_callback = refresh,
553 edit_callback = edit
554 )
555
557
558 if parent is None:
559 parent = wx.GetApp().GetTopWindow()
560
561 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
562 parent = None,
563 id = -1,
564 caption = caption,
565 question = msg,
566 button_defs = [
567 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
568 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
569 ],
570 show_checkbox = False
571 )
572
573 result = dlg.ShowModal()
574 dlg.Destroy()
575
576 if result == wx.ID_YES:
577 return True
578
579 return False
580
582
583 if parent is None:
584 parent = wx.GetApp().GetTopWindow()
585
586
587 def edit(enc_type=None):
588 return edit_encounter_type(parent = parent, encounter_type = enc_type)
589
590 def delete(enc_type=None):
591 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
592 return True
593 gmDispatcher.send (
594 signal = u'statustext',
595 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
596 beep = True
597 )
598 return False
599
600 def refresh(lctrl):
601 enc_types = gmEMRStructItems.get_encounter_types()
602 lctrl.set_string_items(items = enc_types)
603
604 gmListWidgets.get_choices_from_list (
605 parent = parent,
606 msg = _('\nSelect the encounter type you want to edit !\n'),
607 caption = _('Managing encounter types ...'),
608 columns = [_('Local name'), _('Encounter type')],
609 single_selection = True,
610 edit_callback = edit,
611 new_callback = edit,
612 delete_callback = delete,
613 refresh_callback = refresh
614 )
615
625
627 """Phrasewheel to allow selection of encounter type.
628
629 - user input interpreted as encounter type in English or local language
630 - data returned is pk of corresponding encounter type or None
631 """
633
634 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
635
636 mp = gmMatchProvider.cMatchProvider_SQL2 (
637 queries = [
638 u"""
639 select pk, l10n_description from (
640 select distinct on (pk) * from (
641 (select
642 pk,
643 _(description) as l10n_description,
644 1 as rank
645 from
646 clin.encounter_type
647 where
648 _(description) %(fragment_condition)s
649
650 ) union all (
651
652 select
653 pk,
654 _(description) as l10n_description,
655 2 as rank
656 from
657 clin.encounter_type
658 where
659 description %(fragment_condition)s
660 )
661 ) as q_distinct_pk
662 ) as q_ordered order by rank, l10n_description
663 """ ]
664 )
665 mp.setThresholds(2, 4, 6)
666
667 self.matcher = mp
668 self.selection_only = True
669 self.picklist_delay = 50
670
672
677
678
679
680
681
711
724
734
736 self._TCTRL_l10n_name.SetValue(u'')
737 self._TCTRL_name.SetValue(u'')
738 self._TCTRL_name.Enable(True)
739
741 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
742 self._TCTRL_name.SetValue(self.data['description'])
743
744 self._TCTRL_name.Enable(False)
745
747 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
748 self._TCTRL_name.SetValue(self.data['description'])
749 self._TCTRL_name.Enable(True)
750
751
752
753
754
755
757
759 try:
760 self.__encounter = kwargs['encounter']
761 del kwargs['encounter']
762 except KeyError:
763 self.__encounter = None
764
765 try:
766 msg = kwargs['msg']
767 del kwargs['msg']
768 except KeyError:
769 msg = None
770
771 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
772
773 self.refresh(msg = msg)
774
775
776
777 - def refresh(self, encounter=None, msg=None):
816
818
819 if self._PRW_encounter_type.GetData() is None:
820 self._PRW_encounter_type.SetBackgroundColour('pink')
821 self._PRW_encounter_type.Refresh()
822 self._PRW_encounter_type.SetFocus()
823 return False
824 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
825 self._PRW_encounter_type.Refresh()
826
827 if not self._PRW_start.is_valid_timestamp():
828 self._PRW_start.SetFocus()
829 return False
830
831 if not self._PRW_end.is_valid_timestamp():
832 self._PRW_end.SetFocus()
833 return False
834
835 return True
836
838 if not self.__is_valid_for_save():
839 return False
840
841 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
842 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
843 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
844 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
845 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
846 self.__encounter.save_payload()
847
848 return True
849
850
852
854 encounter = kwargs['encounter']
855 del kwargs['encounter']
856
857 try:
858 button_defs = kwargs['button_defs']
859 del kwargs['button_defs']
860 except KeyError:
861 button_defs = None
862
863 try:
864 msg = kwargs['msg']
865 del kwargs['msg']
866 except KeyError:
867 msg = None
868
869 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
870 self.SetSize((450, 280))
871 self.SetMinSize((450, 280))
872
873 if button_defs is not None:
874 self._BTN_save.SetLabel(button_defs[0][0])
875 self._BTN_save.SetToolTipString(button_defs[0][1])
876 self._BTN_close.SetLabel(button_defs[1][0])
877 self._BTN_close.SetToolTipString(button_defs[1][1])
878 self.Refresh()
879
880 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
881
882 self.Fit()
883
890
891
892
902
972
974 """Prepare changing health issue for an episode.
975
976 Checks for two-open-episodes conflict. When this
977 function succeeds, the pk_health_issue has been set
978 on the episode instance and the episode should - for
979 all practical purposes - be ready for save_payload().
980 """
981
982 if not episode['episode_open']:
983 episode['pk_health_issue'] = target_issue['pk_health_issue']
984 if save_to_backend:
985 episode.save_payload()
986 return True
987
988
989 if target_issue is None:
990 episode['pk_health_issue'] = None
991 if save_to_backend:
992 episode.save_payload()
993 return True
994
995
996 db_cfg = gmCfg.cCfgSQL()
997 epi_ttl = int(db_cfg.get2 (
998 option = u'episode.ttl',
999 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1000 bias = 'user',
1001 default = 60
1002 ))
1003 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1004 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1005 existing_epi = target_issue.get_open_episode()
1006
1007
1008 if existing_epi is None:
1009 episode['pk_health_issue'] = target_issue['pk_health_issue']
1010 if save_to_backend:
1011 episode.save_payload()
1012 return True
1013
1014
1015 if existing_epi['pk_episode'] == episode['pk_episode']:
1016 episode['pk_health_issue'] = target_issue['pk_health_issue']
1017 if save_to_backend:
1018 episode.save_payload()
1019 return True
1020
1021
1022 move_range = episode.get_access_range()
1023 exist_range = existing_epi.get_access_range()
1024 question = _(
1025 'You want to associate the running episode:\n\n'
1026 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1027 'with the health issue:\n\n'
1028 ' "%(issue_name)s"\n\n'
1029 'There already is another episode running\n'
1030 'for this health issue:\n\n'
1031 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1032 'However, there can only be one running\n'
1033 'episode per health issue.\n\n'
1034 'Which episode do you want to close ?'
1035 ) % {
1036 'new_epi_name': episode['description'],
1037 'new_epi_start': move_range[0].strftime('%m/%y'),
1038 'new_epi_end': move_range[1].strftime('%m/%y'),
1039 'issue_name': target_issue['description'],
1040 'old_epi_name': existing_epi['description'],
1041 'old_epi_start': exist_range[0].strftime('%m/%y'),
1042 'old_epi_end': exist_range[1].strftime('%m/%y')
1043 }
1044 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1045 parent = None,
1046 id = -1,
1047 caption = _('Resolving two-running-episodes conflict'),
1048 question = question,
1049 button_defs = [
1050 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1051 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1052 ]
1053 )
1054 decision = dlg.ShowModal()
1055
1056 if decision == wx.ID_CANCEL:
1057
1058 return False
1059
1060 elif decision == wx.ID_YES:
1061
1062 existing_epi['episode_open'] = False
1063 existing_epi.save_payload()
1064
1065 elif decision == wx.ID_NO:
1066
1067 episode['episode_open'] = False
1068
1069 else:
1070 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1071
1072 episode['pk_health_issue'] = target_issue['pk_health_issue']
1073 if save_to_backend:
1074 episode.save_payload()
1075 return True
1076
1100
1102 """Let user select an episode *description*.
1103
1104 The user can select an episode description from the previously
1105 used descriptions across all episodes across all patients.
1106
1107 Selection is done with a phrasewheel so the user can
1108 type the episode name and matches will be shown. Typing
1109 "*" will show the entire list of episodes.
1110
1111 If the user types a description not existing yet a
1112 new episode description will be returned.
1113 """
1115
1116 mp = gmMatchProvider.cMatchProvider_SQL2 (
1117 queries = [u"""
1118 select distinct on (description) description, description, 1
1119 from clin.episode
1120 where description %(fragment_condition)s
1121 order by description
1122 limit 30"""
1123 ]
1124 )
1125 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1126 self.matcher = mp
1127
1129 """Let user select an episode.
1130
1131 The user can select an episode from the existing episodes of a
1132 patient. Selection is done with a phrasewheel so the user
1133 can type the episode name and matches will be shown. Typing
1134 "*" will show the entire list of episodes. Closed episodes
1135 will be marked as such. If the user types an episode name not
1136 in the list of existing episodes a new episode can be created
1137 from it if the programmer activated that feature.
1138
1139 If keyword <patient_id> is set to None or left out the control
1140 will listen to patient change signals and therefore act on
1141 gmPerson.gmCurrentPatient() changes.
1142 """
1144
1145 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1146
1147 mp = gmMatchProvider.cMatchProvider_SQL2 (
1148 queries = [
1149 u"""(
1150
1151 select
1152 pk_episode,
1153 coalesce (
1154 description || ' - ' || health_issue,
1155 description
1156 ) as description,
1157 1 as rank
1158 from
1159 clin.v_pat_episodes
1160 where
1161 episode_open is true and
1162 description %(fragment_condition)s
1163 %(ctxt_pat)s
1164
1165 ) union all (
1166
1167 select
1168 pk_episode,
1169 coalesce (
1170 description || _(' (closed)') || ' - ' || health_issue,
1171 description || _(' (closed)')
1172 ) as description,
1173 2 as rank
1174 from
1175 clin.v_pat_episodes
1176 where
1177 description %(fragment_condition)s and
1178 episode_open is false
1179 %(ctxt_pat)s
1180
1181 )
1182 order by rank, description
1183 limit 30"""
1184 ],
1185 context = ctxt
1186 )
1187
1188 try:
1189 kwargs['patient_id']
1190 except KeyError:
1191 kwargs['patient_id'] = None
1192
1193 if kwargs['patient_id'] is None:
1194 self.use_current_patient = True
1195 self.__register_patient_change_signals()
1196 pat = gmPerson.gmCurrentPatient()
1197 if pat.connected:
1198 mp.set_context('pat', pat.ID)
1199 else:
1200 self.use_current_patient = False
1201 self.__patient_id = int(kwargs['patient_id'])
1202 mp.set_context('pat', self.__patient_id)
1203
1204 del kwargs['patient_id']
1205
1206 gmPhraseWheel.cPhraseWheel.__init__ (
1207 self,
1208 *args,
1209 **kwargs
1210 )
1211 self.matcher = mp
1212
1213
1214
1216 if self.use_current_patient:
1217 return False
1218 self.__patient_id = int(patient_id)
1219 self.set_context('pat', self.__patient_id)
1220 return True
1221
1222 - def GetData(self, can_create=False, is_open=False):
1223 if self.data is None:
1224 if can_create:
1225 epi_name = self.GetValue().strip()
1226 if epi_name == u'':
1227 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1228 _log.debug('cannot create episode without name')
1229 else:
1230 if self.use_current_patient:
1231 pat = gmPerson.gmCurrentPatient()
1232 else:
1233 pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
1234
1235 emr = pat.get_emr()
1236 epi = emr.add_episode(episode_name = epi_name, is_open = is_open)
1237 if epi is None:
1238 self.data = None
1239 else:
1240 self.data = epi['pk_episode']
1241
1242 return gmPhraseWheel.cPhraseWheel.GetData(self)
1243
1244
1245
1249
1252
1254 if self.use_current_patient:
1255 patient = gmPerson.gmCurrentPatient()
1256 self.set_context('pat', patient.ID)
1257 return True
1258
1259 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1260
1262
1263 try:
1264 episode = kwargs['episode']
1265 del kwargs['episode']
1266 except KeyError:
1267 episode = None
1268
1269 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
1270 gmEditArea.cGenericEditAreaMixin.__init__(self)
1271
1272 self.data = episode
1273
1274
1275
1277
1278 errors = False
1279
1280 if len(self._PRW_description.GetValue().strip()) == 0:
1281 errors = True
1282 self._PRW_description.display_as_valid(False)
1283 self._PRW_description.SetFocus()
1284 else:
1285 self._PRW_description.display_as_valid(True)
1286 self._PRW_description.Refresh()
1287
1288 return not errors
1289
1291
1292 pat = gmPerson.gmCurrentPatient()
1293 emr = pat.get_emr()
1294
1295 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1296 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1297 epi['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1298
1299 issue_name = self._PRW_issue.GetValue().strip()
1300 if len(issue_name) != 0:
1301 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1302 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1303
1304 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1305 gmDispatcher.send (
1306 signal = 'statustext',
1307 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1308 epi['description'],
1309 issue['description']
1310 )
1311 )
1312 gmEMRStructItems.delete_episode(episode = epi)
1313 return False
1314
1315 epi.save()
1316
1317 self.data = epi
1318 return True
1319
1321
1322 self.data['description'] = self._PRW_description.GetValue().strip()
1323 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1324 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1325
1326 issue_name = self._PRW_issue.GetValue().strip()
1327 if len(issue_name) != 0:
1328 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1329 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1330
1331 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1332 gmDispatcher.send (
1333 signal = 'statustext',
1334 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1335 self.data['description'],
1336 issue['description']
1337 )
1338 )
1339 return False
1340
1341 self.data.save()
1342 return True
1343
1354
1368
1370 self._refresh_as_new()
1371
1372
1373
1383
1385
1386
1387
1389
1390 issues = kwargs['issues']
1391 del kwargs['issues']
1392
1393 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1394
1395 self.SetTitle(_('Select the health issues you are interested in ...'))
1396 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1397
1398 for issue in issues:
1399 if issue['is_confidential']:
1400 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1401 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1402 else:
1403 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1404
1405 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1406 if issue['clinically_relevant']:
1407 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1408 if issue['is_active']:
1409 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1410 if issue['is_cause_of_death']:
1411 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1412
1413 self._LCTRL_items.set_column_widths()
1414 self._LCTRL_items.set_data(data = issues)
1415
1417 """Let the user select a health issue.
1418
1419 The user can select a health issue from the existing issues
1420 of a patient. Selection is done with a phrasewheel so the user
1421 can type the issue name and matches will be shown. Typing
1422 "*" will show the entire list of issues. Inactive issues
1423 will be marked as such. If the user types an issue name not
1424 in the list of existing issues a new issue can be created
1425 from it if the programmer activated that feature.
1426
1427 If keyword <patient_id> is set to None or left out the control
1428 will listen to patient change signals and therefore act on
1429 gmPerson.gmCurrentPatient() changes.
1430 """
1432
1433 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1434
1435 mp = gmMatchProvider.cMatchProvider_SQL2 (
1436
1437 queries = [u"""
1438 (select pk_health_issue, description, 1
1439 from clin.v_health_issues where
1440 is_active is true and
1441 description %(fragment_condition)s and
1442 %(ctxt_pat)s
1443 order by description)
1444
1445 union
1446
1447 (select pk_health_issue, description || _(' (inactive)'), 2
1448 from clin.v_health_issues where
1449 is_active is false and
1450 description %(fragment_condition)s and
1451 %(ctxt_pat)s
1452 order by description)"""
1453 ],
1454 context = ctxt
1455 )
1456
1457 try: kwargs['patient_id']
1458 except KeyError: kwargs['patient_id'] = None
1459
1460 if kwargs['patient_id'] is None:
1461 self.use_current_patient = True
1462 self.__register_patient_change_signals()
1463 pat = gmPerson.gmCurrentPatient()
1464 if pat.connected:
1465 mp.set_context('pat', pat.ID)
1466 else:
1467 self.use_current_patient = False
1468 self.__patient_id = int(kwargs['patient_id'])
1469 mp.set_context('pat', self.__patient_id)
1470
1471 del kwargs['patient_id']
1472
1473 gmPhraseWheel.cPhraseWheel.__init__ (
1474 self,
1475 *args,
1476 **kwargs
1477 )
1478 self.matcher = mp
1479
1480
1481
1483 if self.use_current_patient:
1484 return False
1485 self.__patient_id = int(patient_id)
1486 self.set_context('pat', self.__patient_id)
1487 return True
1488
1489 - def GetData(self, can_create=False, is_open=False):
1507
1508
1509
1513
1516
1518 if self.use_current_patient:
1519 patient = gmPerson.gmCurrentPatient()
1520 self.set_context('pat', patient.ID)
1521 return True
1522
1524
1526 try:
1527 msg = kwargs['message']
1528 except KeyError:
1529 msg = None
1530 del kwargs['message']
1531 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1532 if msg is not None:
1533 self._lbl_message.SetLabel(label=msg)
1534
1545
1546 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1547 """Panel encapsulating health issue edit area functionality."""
1548
1550
1551 try:
1552 issue = kwargs['issue']
1553 except KeyError:
1554 issue = None
1555
1556 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1557
1558 gmEditArea.cGenericEditAreaMixin.__init__(self)
1559
1560
1561 mp = gmMatchProvider.cMatchProvider_SQL2 (
1562 queries = [u"select distinct on (description) description, description from clin.health_issue where description %(fragment_condition)s limit 50"]
1563 )
1564 mp.setThresholds(1, 3, 5)
1565 self._PRW_condition.matcher = mp
1566
1567 mp = gmMatchProvider.cMatchProvider_SQL2 (
1568 queries = [u"""
1569 select distinct on (grouping) grouping, grouping from (
1570
1571 select rank, grouping from ((
1572
1573 select
1574 grouping,
1575 1 as rank
1576 from
1577 clin.health_issue
1578 where
1579 grouping %%(fragment_condition)s
1580 and
1581 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1582
1583 ) union (
1584
1585 select
1586 grouping,
1587 2 as rank
1588 from
1589 clin.health_issue
1590 where
1591 grouping %%(fragment_condition)s
1592
1593 )) as union_result
1594
1595 order by rank
1596
1597 ) as order_result
1598
1599 limit 50""" % gmPerson.gmCurrentPatient().ID
1600 ]
1601 )
1602 mp.setThresholds(1, 3, 5)
1603 self._PRW_grouping.matcher = mp
1604
1605 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1606 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1607
1608 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1609 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1610
1611 self.data = issue
1612
1613
1614
1634
1636 pat = gmPerson.gmCurrentPatient()
1637 emr = pat.get_emr()
1638
1639 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1640
1641 side = u''
1642 if self._ChBOX_left.GetValue():
1643 side += u's'
1644 if self._ChBOX_right.GetValue():
1645 side += u'd'
1646 issue['laterality'] = side
1647
1648 issue['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1649 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1650 issue['is_active'] = self._ChBOX_active.GetValue()
1651 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1652 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
1653 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
1654
1655 age_noted = self._PRW_age_noted.GetData()
1656 if age_noted is not None:
1657 issue['age_noted'] = age_noted
1658
1659 issue.save()
1660
1661 narr = self._TCTRL_notes.GetValue().strip()
1662 if narr != u'':
1663 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = issue['pk_health_issue'])
1664 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
1665
1666 self.data = issue
1667
1668 return True
1669
1671
1672
1673 self.data['description'] = self._PRW_condition.GetValue().strip()
1674
1675 side = u''
1676 if self._ChBOX_left.GetValue():
1677 side += u's'
1678 if self._ChBOX_right.GetValue():
1679 side += u'd'
1680 self.data['laterality'] = side
1681
1682 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1683 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
1684 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
1685 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
1686 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
1687 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
1688
1689 age_noted = self._PRW_age_noted.GetData()
1690 if age_noted is not None:
1691 self.data['age_noted'] = age_noted
1692
1693 self.data.save()
1694
1695 narr = self._TCTRL_notes.GetValue().strip()
1696 if narr != '':
1697 pat = gmPerson.gmCurrentPatient()
1698 emr = pat.get_emr()
1699 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = self.data['pk_health_issue'])
1700 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
1701
1702
1703 return True
1704
1706 self._PRW_condition.SetText()
1707 self._ChBOX_left.SetValue(0)
1708 self._ChBOX_right.SetValue(0)
1709 self._PRW_classification.SetText()
1710 self._PRW_grouping.SetText()
1711 self._TCTRL_notes.SetValue(u'')
1712 self._PRW_age_noted.SetText()
1713 self._PRW_year_noted.SetText()
1714 self._ChBOX_active.SetValue(0)
1715 self._ChBOX_relevant.SetValue(1)
1716 self._ChBOX_is_operation.SetValue(0)
1717 self._ChBOX_confidential.SetValue(0)
1718 self._ChBOX_caused_death.SetValue(0)
1719
1720 return True
1721
1723 self._PRW_condition.SetText(self.data['description'])
1724
1725 lat = gmTools.coalesce(self.data['laterality'], '')
1726 if lat.find('s') == -1:
1727 self._ChBOX_left.SetValue(0)
1728 else:
1729 self._ChBOX_left.SetValue(1)
1730 if lat.find('d') == -1:
1731 self._ChBOX_right.SetValue(0)
1732 else:
1733 self._ChBOX_right.SetValue(1)
1734
1735 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification'])
1736 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
1737 self._TCTRL_notes.SetValue('')
1738
1739 if self.data['age_noted'] is None:
1740 self._PRW_age_noted.SetText()
1741 else:
1742 self._PRW_age_noted.SetText (
1743 value = '%sd' % self.data['age_noted'].days,
1744 data = self.data['age_noted']
1745 )
1746
1747 self._ChBOX_active.SetValue(self.data['is_active'])
1748 self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
1749 self._ChBOX_is_operation.SetValue(0)
1750 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
1751 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
1752
1753
1754
1755
1756
1757 return True
1758
1760 return self._refresh_as_new()
1761
1762
1763
1765
1766 if not self._PRW_age_noted.IsModified():
1767 return True
1768
1769 str_age = self._PRW_age_noted.GetValue().strip()
1770
1771 if str_age == u'':
1772 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1773 return True
1774
1775 age = gmDateTime.str2interval(str_interval = str_age)
1776 pat = gmPerson.gmCurrentPatient()
1777 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
1778
1779 if age is None:
1780 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
1781 self._PRW_age_noted.SetBackgroundColour('pink')
1782 self._PRW_age_noted.Refresh()
1783 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1784 return True
1785
1786 if age >= max_age:
1787 gmDispatcher.send (
1788 signal = 'statustext',
1789 msg = _(
1790 'Health issue cannot have been noted at age %s. Patient is only %s old.'
1791 ) % (age, pat.get_medical_age())
1792 )
1793 self._PRW_age_noted.SetBackgroundColour('pink')
1794 self._PRW_age_noted.Refresh()
1795 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1796 return True
1797
1798 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1799 self._PRW_age_noted.Refresh()
1800 self._PRW_age_noted.SetData(data=age)
1801
1802 fts = gmDateTime.cFuzzyTimestamp (
1803 timestamp = pat['dob'] + age,
1804 accuracy = gmDateTime.acc_months
1805 )
1806 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
1807
1808
1809
1810
1811
1812 return True
1813
1815
1816 if not self._PRW_year_noted.IsModified():
1817 return True
1818
1819 year_noted = self._PRW_year_noted.GetData()
1820
1821 if year_noted is None:
1822 if self._PRW_year_noted.GetValue().strip() == u'':
1823 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1824 return True
1825 self._PRW_year_noted.SetBackgroundColour('pink')
1826 self._PRW_year_noted.Refresh()
1827 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1828 return True
1829
1830 year_noted = year_noted.get_pydt()
1831
1832 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
1833 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
1834 self._PRW_year_noted.SetBackgroundColour('pink')
1835 self._PRW_year_noted.Refresh()
1836 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1837 return True
1838
1839 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1840 self._PRW_year_noted.Refresh()
1841
1842 pat = gmPerson.gmCurrentPatient()
1843 issue_age = year_noted - pat['dob']
1844 str_age = gmDateTime.format_interval_medically(interval = issue_age)
1845 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
1846
1847 return True
1848
1850 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1851 return True
1852
1854 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1855 return True
1856
1857
1858
1860
1862
1863 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1864
1865 self.selection_only = False
1866
1867 mp = gmMatchProvider.cMatchProvider_FixedList (
1868 aSeq = [
1869 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
1870 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
1871 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
1872 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
1873 ]
1874 )
1875 mp.setThresholds(1, 2, 4)
1876 self.matcher = mp
1877
1878 self.SetToolTipString(_(
1879 "The diagnostic classification or grading of this assessment.\n"
1880 "\n"
1881 "This documents how certain one is about this being a true diagnosis."
1882 ))
1883
1884
1885
1886 if __name__ == '__main__':
1887
1888
1890 """
1891 Test application for testing EMR struct widgets
1892 """
1893
1895 """
1896 Create test application UI
1897 """
1898 frame = wx.Frame (
1899 None,
1900 -4,
1901 'Testing EMR struct widgets',
1902 size=wx.Size(600, 400),
1903 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1904 )
1905 filemenu= wx.Menu()
1906 filemenu.AppendSeparator()
1907 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
1908
1909
1910 menuBar = wx.MenuBar()
1911 menuBar.Append(filemenu,"&File")
1912
1913 frame.SetMenuBar(menuBar)
1914
1915 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
1916 wx.DefaultPosition, wx.DefaultSize, 0 )
1917
1918
1919 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
1920
1921
1922 self.__pat = gmPerson.gmCurrentPatient()
1923
1924 frame.Show(1)
1925 return 1
1926
1928 """
1929 Close test aplication
1930 """
1931 self.ExitMainLoop ()
1932
1934 app = wx.PyWidgetTester(size = (200, 300))
1935 emr = pat.get_emr()
1936 enc = emr.active_encounter
1937
1938 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
1939 app.frame.Show(True)
1940 app.MainLoop()
1941 return
1942
1944 app = wx.PyWidgetTester(size = (200, 300))
1945 emr = pat.get_emr()
1946 enc = emr.active_encounter
1947
1948
1949 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
1950 dlg.ShowModal()
1951
1952
1953
1954
1955
1957 app = wx.PyWidgetTester(size = (200, 300))
1958 emr = pat.get_emr()
1959 epi = emr.get_episodes()[0]
1960 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
1961 app.frame.Show(True)
1962 app.MainLoop()
1963
1969
1971 app = wx.PyWidgetTester(size = (400, 40))
1972 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
1973 app.MainLoop()
1974
1976 app = wx.PyWidgetTester(size = (400, 40))
1977 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
1978
1979 app.MainLoop()
1980
1982 app = wx.PyWidgetTester(size = (200, 300))
1983 edit_health_issue(parent=app.frame, issue=None)
1984
1986 app = wx.PyWidgetTester(size = (200, 300))
1987 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
1988 app.MainLoop()
1989
1991 app = wx.PyWidgetTester(size = (200, 300))
1992 edit_procedure(parent=app.frame)
1993
1994
1995 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1996
1997 gmI18N.activate_locale()
1998 gmI18N.install_domain()
1999 gmDateTime.init()
2000
2001
2002 pat = gmPerson.ask_for_patient()
2003 if pat is None:
2004 print "No patient. Exiting gracefully..."
2005 sys.exit(0)
2006 gmPatSearchWidgets.set_active_patient(patient=pat)
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024 test_edit_procedure()
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442