1 """GNUmed EMR structure editors
2
3 This module contains widgets to create and edit EMR structural
4 elements (issues, enconters, episodes).
5
6 This is based on initial work and ideas by Syan <kittylitter@swiftdsl.com.au>
7 and Karsten <Karsten.Hilbert@gmx.net>.
8 """
9
10 __version__ = "$Revision: 1.114 $"
11 __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
12 __license__ = "GPL"
13
14
15 import sys, re, datetime as pydt, logging, time
16
17
18
19 import wx
20 import wx.lib.pubsub as wxps
21
22
23
24 if __name__ == '__main__':
25 sys.path.insert(0, '../../')
26 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
27 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery
28 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
29 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
30 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
31 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl, wxgEncounterEditAreaDlg
32 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
33 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
34
35
36 _log = logging.getLogger('gm.ui')
37 _log.info(__version__)
38
39
40
51
52 def delete(procedure=None):
53 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
54 return True
55
56 gmDispatcher.send (
57 signal = u'statustext',
58 msg = _('Cannot delete performed procedure.'),
59 beep = True
60 )
61 return False
62
63 def refresh(lctrl):
64 procs = emr.get_performed_procedures()
65
66 items = [
67 [
68 p['clin_when'].strftime('%Y-%m-%d'),
69 p['clin_where'],
70 p['episode'],
71 p['performed_procedure']
72 ] for p in procs
73 ]
74 lctrl.set_string_items(items = items)
75 lctrl.set_data(data = procs)
76
77 gmListWidgets.get_choices_from_list (
78 parent = parent,
79 msg = _('\nSelect the procedure you want to edit !\n'),
80 caption = _('Editing performed procedures ...'),
81 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
82 single_selection = True,
83 edit_callback = edit,
84 new_callback = edit,
85 delete_callback = delete,
86 refresh_callback = refresh
87 )
88
100
101 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
102
103 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
104
113
115 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
116 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
117
118
119 mp = gmMatchProvider.cMatchProvider_SQL2 (
120 queries = [
121 u"""
122 select distinct on (clin_where) clin_where, clin_where
123 from clin.procedure
124 where clin_where %(fragment_condition)s
125 order by clin_where
126 limit 25
127 """ ]
128 )
129 mp.setThresholds(2, 4, 6)
130 self._PRW_location.matcher = mp
131
132
133 mp = gmMatchProvider.cMatchProvider_SQL2 (
134 queries = [
135 u"""
136 select distinct on (narrative) narrative, narrative
137 from clin.procedure
138 where narrative %(fragment_condition)s
139 order by narrative
140 limit 25
141 """ ]
142 )
143 mp.setThresholds(2, 4, 6)
144 self._PRW_procedure.matcher = mp
145
147 if self._PRW_hospital_stay.GetData() is None:
148 self._PRW_hospital_stay.SetText()
149 self._PRW_location.Enable(True)
150 self._PRW_episode.Enable(True)
151 else:
152 self._PRW_location.SetText()
153 self._PRW_location.Enable(False)
154 self._PRW_episode.SetText()
155 self._PRW_episode.Enable(False)
156
158 if self._PRW_location.GetValue().strip() == u'':
159 self._PRW_hospital_stay.Enable(True)
160
161 else:
162 self._PRW_hospital_stay.SetText()
163 self._PRW_hospital_stay.Enable(False)
164
165
166
167
169
170 has_errors = False
171
172 if not self._DPRW_date.is_valid_timestamp():
173 self._DPRW_date.display_as_valid(False)
174 has_errors = True
175 else:
176 self._DPRW_date.display_as_valid(True)
177
178 if self._PRW_hospital_stay.GetData() is None:
179 if self._PRW_episode.GetData() is None:
180 self._PRW_episode.display_as_valid(False)
181 has_errors = True
182 else:
183 self._PRW_episode.display_as_valid(True)
184 else:
185 self._PRW_episode.display_as_valid(True)
186
187 if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''):
188 self._PRW_procedure.display_as_valid(False)
189 has_errors = True
190 else:
191 self._PRW_procedure.display_as_valid(True)
192
193 invalid_location = (
194 (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'')
195 or
196 (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'')
197 )
198 if invalid_location:
199 self._PRW_hospital_stay.display_as_valid(False)
200 self._PRW_location.display_as_valid(False)
201 has_errors = True
202 else:
203 self._PRW_hospital_stay.display_as_valid(True)
204 self._PRW_location.display_as_valid(True)
205
206 wxps.Publisher().sendMessage (
207 topic = 'statustext',
208 data = {'msg': _('Cannot save procedure.'), 'beep': True}
209 )
210
211 return (has_errors is False)
212
239
241 self.data['clin_when'] = self._DPRW_date.data.get_pydt()
242
243 if self._PRW_hospital_stay.GetData() is None:
244 self.data['pk_hospital_stay'] = None
245 self.data['clin_where'] = self._PRW_location.GetValue().strip()
246 self.data['pk_episode'] = self._PRW_episode.GetData()
247 else:
248 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
249 self.data['clin_where'] = None
250 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
251 self.data['pk_episode'] = stay['pk_episode']
252
253 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
254
255 self.data.save()
256 return True
257
266
280
292
296
297
298
309
310 def delete(stay=None):
311 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
312 return True
313 gmDispatcher.send (
314 signal = u'statustext',
315 msg = _('Cannot delete hospital stay.'),
316 beep = True
317 )
318 return False
319
320 def refresh(lctrl):
321 stays = emr.get_hospital_stays()
322 items = [
323 [
324 s['admission'].strftime('%Y-%m-%d'),
325 gmTools.coalesce(s['discharge'], u''),
326 s['episode'],
327 gmTools.coalesce(s['hospital'], u'')
328 ] for s in stays
329 ]
330 lctrl.set_string_items(items = items)
331 lctrl.set_data(data = stays)
332
333 gmListWidgets.get_choices_from_list (
334 parent = parent,
335 msg = _('\nSelect the hospital stay you want to edit !\n'),
336 caption = _('Editing hospital stays ...'),
337 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
338 single_selection = True,
339 edit_callback = edit,
340 new_callback = edit,
341 delete_callback = delete,
342 refresh_callback = refresh
343 )
344
345
357
359 """Phrasewheel to allow selection of a hospital stay.
360 """
362
363 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
364
365 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
366
367 mp = gmMatchProvider.cMatchProvider_SQL2 (
368 queries = [
369 u"""
370 select
371 pk_hospital_stay,
372 descr
373 from (
374 select distinct on (pk_hospital_stay)
375 pk_hospital_stay,
376 descr
377 from
378 (select
379 pk_hospital_stay,
380 (
381 to_char(admission, 'YYYY-Mon-DD')
382 || coalesce((' (' || hospital || '):'), ': ')
383 || episode
384 || coalesce((' (' || health_issue || ')'), '')
385 ) as descr
386 from
387 clin.v_pat_hospital_stays
388 where
389 %(ctxt_pat)s
390
391 hospital %(fragment_condition)s
392 or
393 episode %(fragment_condition)s
394 or
395 health_issue %(fragment_condition)s
396 ) as the_stays
397 ) as distinct_stays
398 order by descr
399 limit 25
400 """ ],
401 context = ctxt
402 )
403 mp.setThresholds(3, 4, 6)
404 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
405
406 self.matcher = mp
407 self.selection_only = True
408
409 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
410
411 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
412
416
417
418
420 if not self._DP_admission.GetValue().IsValid():
421 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
422 wxps.Publisher().sendMessage (
423 topic = 'statustext',
424 data = {'msg': _('Missing admission data. Cannot save hospital stay.'), 'beep': True}
425 )
426 return False
427 else:
428 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
429
430 if self._DP_discharge.GetValue().IsValid():
431 if not self._DP_discharge.GetValue().IsLaterThan(self._DP_admission.GetValue()):
432 self._DP_discharge.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
433 wxps.Publisher().sendMessage (
434 topic = 'statustext',
435 data = {'msg': _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), 'beep': True}
436 )
437 return False
438
439 return True
440
458
469
474
475
487
489 print "this was not expected to be used in this edit area"
490
491
492
501
503
504 if parent is None:
505 parent = wx.GetApp().GetTopWindow()
506
507
508 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
509 dlg.ShowModal()
510
511 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None):
512
513 if patient is None:
514 patient = gmPerson.gmCurrentPatient()
515
516 if not patient.connected:
517 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
518 return False
519
520 if parent is None:
521 parent = wx.GetApp().GetTopWindow()
522
523 emr = patient.get_emr()
524
525
526 def refresh(lctrl):
527 if encounters is not None:
528 encs = encounters
529 else:
530 encs = emr.get_encounters()
531
532 items = [
533 [
534 e['started'].strftime('%x %H:%M'),
535 e['last_affirmed'].strftime('%H:%M'),
536 e['l10n_type'],
537 gmTools.coalesce(e['reason_for_encounter'], u''),
538 gmTools.coalesce(e['assessment_of_encounter'], u''),
539 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
540 e['pk_encounter']
541 ] for e in encs
542 ]
543
544 lctrl.set_string_items(items = items)
545 lctrl.set_data(data = encs)
546
547 def edit(enc = None):
548 return edit_encounter(parent = parent, encounter = enc)
549
550 return gmListWidgets.get_choices_from_list (
551 parent = parent,
552 msg = _('\nBelow find the relevant encounters of the patient.\n'),
553 caption = _('Encounters ...'),
554 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
555 can_return_empty = True,
556 single_selection = single_selection,
557 refresh_callback = refresh,
558 edit_callback = edit
559 )
560
562 """This is used as the callback when the EMR detects that the
563 patient was here rather recently and wants to ask the
564 provider whether to continue the recent encounter.
565 """
566 if parent is None:
567 parent = wx.GetApp().GetTopWindow()
568
569 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
570 parent = None,
571 id = -1,
572 caption = caption,
573 question = msg,
574 button_defs = [
575 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
576 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
577 ],
578 show_checkbox = False
579 )
580
581 result = dlg.ShowModal()
582 dlg.Destroy()
583
584 if result == wx.ID_YES:
585 return True
586
587 return False
588
590
591 if parent is None:
592 parent = wx.GetApp().GetTopWindow()
593
594
595 def edit(enc_type=None):
596 return edit_encounter_type(parent = parent, encounter_type = enc_type)
597
598 def delete(enc_type=None):
599 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
600 return True
601 gmDispatcher.send (
602 signal = u'statustext',
603 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
604 beep = True
605 )
606 return False
607
608 def refresh(lctrl):
609 enc_types = gmEMRStructItems.get_encounter_types()
610 lctrl.set_string_items(items = enc_types)
611
612 gmListWidgets.get_choices_from_list (
613 parent = parent,
614 msg = _('\nSelect the encounter type you want to edit !\n'),
615 caption = _('Managing encounter types ...'),
616 columns = [_('Local name'), _('Encounter type')],
617 single_selection = True,
618 edit_callback = edit,
619 new_callback = edit,
620 delete_callback = delete,
621 refresh_callback = refresh
622 )
623
633
635 """Phrasewheel to allow selection of encounter type.
636
637 - user input interpreted as encounter type in English or local language
638 - data returned is pk of corresponding encounter type or None
639 """
641
642 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
643
644 mp = gmMatchProvider.cMatchProvider_SQL2 (
645 queries = [
646 u"""
647 select pk, l10n_description from (
648 select distinct on (pk) * from (
649 (select
650 pk,
651 _(description) as l10n_description,
652 1 as rank
653 from
654 clin.encounter_type
655 where
656 _(description) %(fragment_condition)s
657
658 ) union all (
659
660 select
661 pk,
662 _(description) as l10n_description,
663 2 as rank
664 from
665 clin.encounter_type
666 where
667 description %(fragment_condition)s
668 )
669 ) as q_distinct_pk
670 ) as q_ordered order by rank, l10n_description
671 """ ]
672 )
673 mp.setThresholds(2, 4, 6)
674
675 self.matcher = mp
676 self.selection_only = True
677 self.picklist_delay = 50
678
680
685
686
687
688
689
719
732
742
744 self._TCTRL_l10n_name.SetValue(u'')
745 self._TCTRL_name.SetValue(u'')
746 self._TCTRL_name.Enable(True)
747
749 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
750 self._TCTRL_name.SetValue(self.data['description'])
751
752 self._TCTRL_name.Enable(False)
753
755 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
756 self._TCTRL_name.SetValue(self.data['description'])
757 self._TCTRL_name.Enable(True)
758
759
760
761
762
763
765
767 try:
768 self.__encounter = kwargs['encounter']
769 del kwargs['encounter']
770 except KeyError:
771 self.__encounter = None
772
773 try:
774 msg = kwargs['msg']
775 del kwargs['msg']
776 except KeyError:
777 msg = None
778
779 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
780
781 self.refresh(msg = msg)
782
783
784
785 - def refresh(self, encounter=None, msg=None):
824
826
827 if self._PRW_encounter_type.GetData() is None:
828 self._PRW_encounter_type.SetBackgroundColour('pink')
829 self._PRW_encounter_type.Refresh()
830 self._PRW_encounter_type.SetFocus()
831 return False
832 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
833 self._PRW_encounter_type.Refresh()
834
835 if not self._PRW_start.is_valid_timestamp():
836 self._PRW_start.SetFocus()
837 return False
838
839 if not self._PRW_end.is_valid_timestamp():
840 self._PRW_end.SetFocus()
841 return False
842
843 return True
844
846 if not self.__is_valid_for_save():
847 return False
848
849 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
850 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
851 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
852 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
853 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
854 self.__encounter.save_payload()
855
856 return True
857
858
860
862 encounter = kwargs['encounter']
863 del kwargs['encounter']
864
865 try:
866 button_defs = kwargs['button_defs']
867 del kwargs['button_defs']
868 except KeyError:
869 button_defs = None
870
871 try:
872 msg = kwargs['msg']
873 del kwargs['msg']
874 except KeyError:
875 msg = None
876
877 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
878 self.SetSize((450, 280))
879 self.SetMinSize((450, 280))
880
881 if button_defs is not None:
882 self._BTN_save.SetLabel(button_defs[0][0])
883 self._BTN_save.SetToolTipString(button_defs[0][1])
884 self._BTN_close.SetLabel(button_defs[1][0])
885 self._BTN_close.SetToolTipString(button_defs[1][1])
886 self.Refresh()
887
888 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
889
890 self.Fit()
891
898
899
900
910
980
982 """Prepare changing health issue for an episode.
983
984 Checks for two-open-episodes conflict. When this
985 function succeeds, the pk_health_issue has been set
986 on the episode instance and the episode should - for
987 all practical purposes - be ready for save_payload().
988 """
989
990 if not episode['episode_open']:
991 episode['pk_health_issue'] = target_issue['pk_health_issue']
992 if save_to_backend:
993 episode.save_payload()
994 return True
995
996
997 if target_issue is None:
998 episode['pk_health_issue'] = None
999 if save_to_backend:
1000 episode.save_payload()
1001 return True
1002
1003
1004 db_cfg = gmCfg.cCfgSQL()
1005 epi_ttl = int(db_cfg.get2 (
1006 option = u'episode.ttl',
1007 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1008 bias = 'user',
1009 default = 60
1010 ))
1011 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1012 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1013 existing_epi = target_issue.get_open_episode()
1014
1015
1016 if existing_epi is None:
1017 episode['pk_health_issue'] = target_issue['pk_health_issue']
1018 if save_to_backend:
1019 episode.save_payload()
1020 return True
1021
1022
1023 if existing_epi['pk_episode'] == episode['pk_episode']:
1024 episode['pk_health_issue'] = target_issue['pk_health_issue']
1025 if save_to_backend:
1026 episode.save_payload()
1027 return True
1028
1029
1030 move_range = episode.get_access_range()
1031 exist_range = existing_epi.get_access_range()
1032 question = _(
1033 'You want to associate the running episode:\n\n'
1034 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1035 'with the health issue:\n\n'
1036 ' "%(issue_name)s"\n\n'
1037 'There already is another episode running\n'
1038 'for this health issue:\n\n'
1039 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1040 'However, there can only be one running\n'
1041 'episode per health issue.\n\n'
1042 'Which episode do you want to close ?'
1043 ) % {
1044 'new_epi_name': episode['description'],
1045 'new_epi_start': move_range[0].strftime('%m/%y'),
1046 'new_epi_end': move_range[1].strftime('%m/%y'),
1047 'issue_name': target_issue['description'],
1048 'old_epi_name': existing_epi['description'],
1049 'old_epi_start': exist_range[0].strftime('%m/%y'),
1050 'old_epi_end': exist_range[1].strftime('%m/%y')
1051 }
1052 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1053 parent = None,
1054 id = -1,
1055 caption = _('Resolving two-running-episodes conflict'),
1056 question = question,
1057 button_defs = [
1058 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1059 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1060 ]
1061 )
1062 decision = dlg.ShowModal()
1063
1064 if decision == wx.ID_CANCEL:
1065
1066 return False
1067
1068 elif decision == wx.ID_YES:
1069
1070 existing_epi['episode_open'] = False
1071 existing_epi.save_payload()
1072
1073 elif decision == wx.ID_NO:
1074
1075 episode['episode_open'] = False
1076
1077 else:
1078 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1079
1080 episode['pk_health_issue'] = target_issue['pk_health_issue']
1081 if save_to_backend:
1082 episode.save_payload()
1083 return True
1084
1108
1110 """Let user select an episode *description*.
1111
1112 The user can select an episode description from the previously
1113 used descriptions across all episodes across all patients.
1114
1115 Selection is done with a phrasewheel so the user can
1116 type the episode name and matches will be shown. Typing
1117 "*" will show the entire list of episodes.
1118
1119 If the user types a description not existing yet a
1120 new episode description will be returned.
1121 """
1123
1124 mp = gmMatchProvider.cMatchProvider_SQL2 (
1125 queries = [u"""
1126 select distinct on (description) description, description, 1
1127 from clin.episode
1128 where description %(fragment_condition)s
1129 order by description
1130 limit 30"""
1131 ]
1132 )
1133 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1134 self.matcher = mp
1135
1137 """Let user select an episode.
1138
1139 The user can select an episode from the existing episodes of a
1140 patient. Selection is done with a phrasewheel so the user
1141 can type the episode name and matches will be shown. Typing
1142 "*" will show the entire list of episodes. Closed episodes
1143 will be marked as such. If the user types an episode name not
1144 in the list of existing episodes a new episode can be created
1145 from it if the programmer activated that feature.
1146
1147 If keyword <patient_id> is set to None or left out the control
1148 will listen to patient change signals and therefore act on
1149 gmPerson.gmCurrentPatient() changes.
1150 """
1152
1153 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1154
1155 mp = gmMatchProvider.cMatchProvider_SQL2 (
1156 queries = [
1157 u"""(
1158
1159 select
1160 pk_episode,
1161 coalesce (
1162 description || ' - ' || health_issue,
1163 description
1164 ) as description,
1165 1 as rank
1166 from
1167 clin.v_pat_episodes
1168 where
1169 episode_open is true and
1170 description %(fragment_condition)s
1171 %(ctxt_pat)s
1172
1173 ) union all (
1174
1175 select
1176 pk_episode,
1177 coalesce (
1178 description || _(' (closed)') || ' - ' || health_issue,
1179 description || _(' (closed)')
1180 ) as description,
1181 2 as rank
1182 from
1183 clin.v_pat_episodes
1184 where
1185 description %(fragment_condition)s and
1186 episode_open is false
1187 %(ctxt_pat)s
1188
1189 )
1190 order by rank, description
1191 limit 30"""
1192 ],
1193 context = ctxt
1194 )
1195
1196 try:
1197 kwargs['patient_id']
1198 except KeyError:
1199 kwargs['patient_id'] = None
1200
1201 if kwargs['patient_id'] is None:
1202 self.use_current_patient = True
1203 self.__register_patient_change_signals()
1204 pat = gmPerson.gmCurrentPatient()
1205 if pat.connected:
1206 mp.set_context('pat', pat.ID)
1207 else:
1208 self.use_current_patient = False
1209 self.__patient_id = int(kwargs['patient_id'])
1210 mp.set_context('pat', self.__patient_id)
1211
1212 del kwargs['patient_id']
1213
1214 gmPhraseWheel.cPhraseWheel.__init__ (
1215 self,
1216 *args,
1217 **kwargs
1218 )
1219 self.matcher = mp
1220
1221
1222
1224 if self.use_current_patient:
1225 return False
1226 self.__patient_id = int(patient_id)
1227 self.set_context('pat', self.__patient_id)
1228 return True
1229
1230 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1234
1236
1237 epi_name = self.GetValue().strip()
1238 if epi_name == u'':
1239 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1240 _log.debug('cannot create episode without name')
1241 return
1242
1243 if self.use_current_patient:
1244 pat = gmPerson.gmCurrentPatient()
1245 else:
1246 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1247
1248 emr = pat.get_emr()
1249 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1250 if epi is None:
1251 self.data = None
1252 else:
1253 self.data = epi['pk_episode']
1254
1257
1258
1259
1263
1266
1268 if self.use_current_patient:
1269 patient = gmPerson.gmCurrentPatient()
1270 self.set_context('pat', patient.ID)
1271 return True
1272
1273 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1274
1287
1288
1289
1291
1292 errors = False
1293
1294 if len(self._PRW_description.GetValue().strip()) == 0:
1295 errors = True
1296 self._PRW_description.display_as_valid(False)
1297 self._PRW_description.SetFocus()
1298 else:
1299 self._PRW_description.display_as_valid(True)
1300 self._PRW_description.Refresh()
1301
1302 return not errors
1303
1305
1306 pat = gmPerson.gmCurrentPatient()
1307 emr = pat.get_emr()
1308
1309 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1310 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1311 epi['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1312
1313 issue_name = self._PRW_issue.GetValue().strip()
1314 if len(issue_name) != 0:
1315 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1316 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1317
1318 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1319 gmDispatcher.send (
1320 signal = 'statustext',
1321 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1322 epi['description'],
1323 issue['description']
1324 )
1325 )
1326 gmEMRStructItems.delete_episode(episode = epi)
1327 return False
1328
1329 epi.save()
1330
1331 self.data = epi
1332 return True
1333
1335
1336 self.data['description'] = self._PRW_description.GetValue().strip()
1337 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1338 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1339
1340 issue_name = self._PRW_issue.GetValue().strip()
1341 if len(issue_name) != 0:
1342 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1343 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1344
1345 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1346 gmDispatcher.send (
1347 signal = 'statustext',
1348 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1349 self.data['description'],
1350 issue['description']
1351 )
1352 )
1353 return False
1354
1355 self.data.save()
1356 return True
1357
1368
1382
1384 self._refresh_as_new()
1385
1386
1387
1397
1399
1400
1401
1403
1404 issues = kwargs['issues']
1405 del kwargs['issues']
1406
1407 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1408
1409 self.SetTitle(_('Select the health issues you are interested in ...'))
1410 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1411
1412 for issue in issues:
1413 if issue['is_confidential']:
1414 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1415 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1416 else:
1417 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1418
1419 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1420 if issue['clinically_relevant']:
1421 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1422 if issue['is_active']:
1423 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1424 if issue['is_cause_of_death']:
1425 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1426
1427 self._LCTRL_items.set_column_widths()
1428 self._LCTRL_items.set_data(data = issues)
1429
1431 """Let the user select a health issue.
1432
1433 The user can select a health issue from the existing issues
1434 of a patient. Selection is done with a phrasewheel so the user
1435 can type the issue name and matches will be shown. Typing
1436 "*" will show the entire list of issues. Inactive issues
1437 will be marked as such. If the user types an issue name not
1438 in the list of existing issues a new issue can be created
1439 from it if the programmer activated that feature.
1440
1441 If keyword <patient_id> is set to None or left out the control
1442 will listen to patient change signals and therefore act on
1443 gmPerson.gmCurrentPatient() changes.
1444 """
1446
1447 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1448
1449 mp = gmMatchProvider.cMatchProvider_SQL2 (
1450
1451 queries = [u"""
1452 (select pk_health_issue, description, 1
1453 from clin.v_health_issues where
1454 is_active is true and
1455 description %(fragment_condition)s and
1456 %(ctxt_pat)s
1457 order by description)
1458
1459 union
1460
1461 (select pk_health_issue, description || _(' (inactive)'), 2
1462 from clin.v_health_issues where
1463 is_active is false and
1464 description %(fragment_condition)s and
1465 %(ctxt_pat)s
1466 order by description)"""
1467 ],
1468 context = ctxt
1469 )
1470
1471 try: kwargs['patient_id']
1472 except KeyError: kwargs['patient_id'] = None
1473
1474 if kwargs['patient_id'] is None:
1475 self.use_current_patient = True
1476 self.__register_patient_change_signals()
1477 pat = gmPerson.gmCurrentPatient()
1478 if pat.connected:
1479 mp.set_context('pat', pat.ID)
1480 else:
1481 self.use_current_patient = False
1482 self.__patient_id = int(kwargs['patient_id'])
1483 mp.set_context('pat', self.__patient_id)
1484
1485 del kwargs['patient_id']
1486
1487 gmPhraseWheel.cPhraseWheel.__init__ (
1488 self,
1489 *args,
1490 **kwargs
1491 )
1492 self.matcher = mp
1493
1494
1495
1497 if self.use_current_patient:
1498 return False
1499 self.__patient_id = int(patient_id)
1500 self.set_context('pat', self.__patient_id)
1501 return True
1502
1503 - def GetData(self, can_create=False, is_open=False):
1521
1522
1523
1527
1530
1532 if self.use_current_patient:
1533 patient = gmPerson.gmCurrentPatient()
1534 self.set_context('pat', patient.ID)
1535 return True
1536
1538
1540 try:
1541 msg = kwargs['message']
1542 except KeyError:
1543 msg = None
1544 del kwargs['message']
1545 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1546 if msg is not None:
1547 self._lbl_message.SetLabel(label=msg)
1548
1559
1560 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1561 """Panel encapsulating health issue edit area functionality."""
1562
1564
1565 try:
1566 issue = kwargs['issue']
1567 except KeyError:
1568 issue = None
1569
1570 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1571
1572 gmEditArea.cGenericEditAreaMixin.__init__(self)
1573
1574
1575 mp = gmMatchProvider.cMatchProvider_SQL2 (
1576 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1577 )
1578 mp.setThresholds(1, 3, 5)
1579 self._PRW_condition.matcher = mp
1580
1581 mp = gmMatchProvider.cMatchProvider_SQL2 (
1582 queries = [u"""
1583 select distinct on (grouping) grouping, grouping from (
1584
1585 select rank, grouping from ((
1586
1587 select
1588 grouping,
1589 1 as rank
1590 from
1591 clin.health_issue
1592 where
1593 grouping %%(fragment_condition)s
1594 and
1595 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1596
1597 ) union (
1598
1599 select
1600 grouping,
1601 2 as rank
1602 from
1603 clin.health_issue
1604 where
1605 grouping %%(fragment_condition)s
1606
1607 )) as union_result
1608
1609 order by rank
1610
1611 ) as order_result
1612
1613 limit 50""" % gmPerson.gmCurrentPatient().ID
1614 ]
1615 )
1616 mp.setThresholds(1, 3, 5)
1617 self._PRW_grouping.matcher = mp
1618
1619 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1620 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1621
1622 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1623 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1624
1625 self.data = issue
1626
1627
1628
1648
1650 pat = gmPerson.gmCurrentPatient()
1651 emr = pat.get_emr()
1652
1653 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1654
1655 side = u''
1656 if self._ChBOX_left.GetValue():
1657 side += u's'
1658 if self._ChBOX_right.GetValue():
1659 side += u'd'
1660 issue['laterality'] = side
1661
1662 issue['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1663 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1664 issue['is_active'] = self._ChBOX_active.GetValue()
1665 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1666 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
1667 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
1668
1669 age_noted = self._PRW_age_noted.GetData()
1670 if age_noted is not None:
1671 issue['age_noted'] = age_noted
1672
1673 issue.save()
1674
1675 narr = self._TCTRL_notes.GetValue().strip()
1676 if narr != u'':
1677 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = issue['pk_health_issue'])
1678 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
1679
1680 self.data = issue
1681
1682 return True
1683
1685
1686
1687 self.data['description'] = self._PRW_condition.GetValue().strip()
1688
1689 side = u''
1690 if self._ChBOX_left.GetValue():
1691 side += u's'
1692 if self._ChBOX_right.GetValue():
1693 side += u'd'
1694 self.data['laterality'] = side
1695
1696 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1697 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
1698 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
1699 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
1700 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
1701 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
1702
1703 age_noted = self._PRW_age_noted.GetData()
1704 if age_noted is not None:
1705 self.data['age_noted'] = age_noted
1706
1707 self.data.save()
1708
1709 narr = self._TCTRL_notes.GetValue().strip()
1710 if narr != '':
1711 pat = gmPerson.gmCurrentPatient()
1712 emr = pat.get_emr()
1713 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = self.data['pk_health_issue'])
1714 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
1715
1716
1717 return True
1718
1720 self._PRW_condition.SetText()
1721 self._ChBOX_left.SetValue(0)
1722 self._ChBOX_right.SetValue(0)
1723 self._PRW_classification.SetText()
1724 self._PRW_grouping.SetText()
1725 self._TCTRL_notes.SetValue(u'')
1726 self._PRW_age_noted.SetText()
1727 self._PRW_year_noted.SetText()
1728 self._ChBOX_active.SetValue(0)
1729 self._ChBOX_relevant.SetValue(1)
1730 self._ChBOX_is_operation.SetValue(0)
1731 self._ChBOX_confidential.SetValue(0)
1732 self._ChBOX_caused_death.SetValue(0)
1733
1734 return True
1735
1737 self._PRW_condition.SetText(self.data['description'])
1738
1739 lat = gmTools.coalesce(self.data['laterality'], '')
1740 if lat.find('s') == -1:
1741 self._ChBOX_left.SetValue(0)
1742 else:
1743 self._ChBOX_left.SetValue(1)
1744 if lat.find('d') == -1:
1745 self._ChBOX_right.SetValue(0)
1746 else:
1747 self._ChBOX_right.SetValue(1)
1748
1749 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification'])
1750 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
1751 self._TCTRL_notes.SetValue('')
1752
1753 if self.data['age_noted'] is None:
1754 self._PRW_age_noted.SetText()
1755 else:
1756 self._PRW_age_noted.SetText (
1757 value = '%sd' % self.data['age_noted'].days,
1758 data = self.data['age_noted']
1759 )
1760
1761 self._ChBOX_active.SetValue(self.data['is_active'])
1762 self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
1763 self._ChBOX_is_operation.SetValue(0)
1764 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
1765 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
1766
1767
1768
1769
1770
1771 return True
1772
1774 return self._refresh_as_new()
1775
1776
1777
1779
1780 if not self._PRW_age_noted.IsModified():
1781 return True
1782
1783 str_age = self._PRW_age_noted.GetValue().strip()
1784
1785 if str_age == u'':
1786 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1787 return True
1788
1789 age = gmDateTime.str2interval(str_interval = str_age)
1790
1791 if age is None:
1792 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
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 pat = gmPerson.gmCurrentPatient()
1799 if pat['dob'] is not None:
1800 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
1801
1802 if age >= max_age:
1803 gmDispatcher.send (
1804 signal = 'statustext',
1805 msg = _(
1806 'Health issue cannot have been noted at age %s. Patient is only %s old.'
1807 ) % (age, pat.get_medical_age())
1808 )
1809 self._PRW_age_noted.SetBackgroundColour('pink')
1810 self._PRW_age_noted.Refresh()
1811 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1812 return True
1813
1814 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1815 self._PRW_age_noted.Refresh()
1816 self._PRW_age_noted.SetData(data=age)
1817
1818 if pat['dob'] is not None:
1819 fts = gmDateTime.cFuzzyTimestamp (
1820 timestamp = pat['dob'] + age,
1821 accuracy = gmDateTime.acc_months
1822 )
1823 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
1824
1825
1826
1827
1828
1829 return True
1830
1832
1833 if not self._PRW_year_noted.IsModified():
1834 return True
1835
1836 year_noted = self._PRW_year_noted.GetData()
1837
1838 if year_noted is None:
1839 if self._PRW_year_noted.GetValue().strip() == u'':
1840 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1841 return True
1842 self._PRW_year_noted.SetBackgroundColour('pink')
1843 self._PRW_year_noted.Refresh()
1844 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1845 return True
1846
1847 year_noted = year_noted.get_pydt()
1848
1849 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
1850 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
1851 self._PRW_year_noted.SetBackgroundColour('pink')
1852 self._PRW_year_noted.Refresh()
1853 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1854 return True
1855
1856 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1857 self._PRW_year_noted.Refresh()
1858
1859 pat = gmPerson.gmCurrentPatient()
1860 if pat['dob'] is not None:
1861 issue_age = year_noted - pat['dob']
1862 str_age = gmDateTime.format_interval_medically(interval = issue_age)
1863 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
1864
1865 return True
1866
1868 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1869 return True
1870
1872 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1873 return True
1874
1875
1876
1878
1880
1881 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1882
1883 self.selection_only = False
1884
1885 mp = gmMatchProvider.cMatchProvider_FixedList (
1886 aSeq = [
1887 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
1888 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
1889 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
1890 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
1891 ]
1892 )
1893 mp.setThresholds(1, 2, 4)
1894 self.matcher = mp
1895
1896 self.SetToolTipString(_(
1897 "The diagnostic classification or grading of this assessment.\n"
1898 "\n"
1899 "This documents how certain one is about this being a true diagnosis."
1900 ))
1901
1902
1903
1904 if __name__ == '__main__':
1905
1906
1908 """
1909 Test application for testing EMR struct widgets
1910 """
1911
1913 """
1914 Create test application UI
1915 """
1916 frame = wx.Frame (
1917 None,
1918 -4,
1919 'Testing EMR struct widgets',
1920 size=wx.Size(600, 400),
1921 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1922 )
1923 filemenu= wx.Menu()
1924 filemenu.AppendSeparator()
1925 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
1926
1927
1928 menuBar = wx.MenuBar()
1929 menuBar.Append(filemenu,"&File")
1930
1931 frame.SetMenuBar(menuBar)
1932
1933 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
1934 wx.DefaultPosition, wx.DefaultSize, 0 )
1935
1936
1937 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
1938
1939
1940 self.__pat = gmPerson.gmCurrentPatient()
1941
1942 frame.Show(1)
1943 return 1
1944
1946 """
1947 Close test aplication
1948 """
1949 self.ExitMainLoop ()
1950
1952 app = wx.PyWidgetTester(size = (200, 300))
1953 emr = pat.get_emr()
1954 enc = emr.active_encounter
1955
1956 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
1957 app.frame.Show(True)
1958 app.MainLoop()
1959 return
1960
1962 app = wx.PyWidgetTester(size = (200, 300))
1963 emr = pat.get_emr()
1964 enc = emr.active_encounter
1965
1966
1967 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
1968 dlg.ShowModal()
1969
1970
1971
1972
1973
1975 app = wx.PyWidgetTester(size = (200, 300))
1976 emr = pat.get_emr()
1977 epi = emr.get_episodes()[0]
1978 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
1979 app.frame.Show(True)
1980 app.MainLoop()
1981
1987
1989 app = wx.PyWidgetTester(size = (400, 40))
1990 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
1991 app.MainLoop()
1992
1994 app = wx.PyWidgetTester(size = (400, 40))
1995 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
1996
1997 app.MainLoop()
1998
2000 app = wx.PyWidgetTester(size = (200, 300))
2001 edit_health_issue(parent=app.frame, issue=None)
2002
2004 app = wx.PyWidgetTester(size = (200, 300))
2005 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2006 app.MainLoop()
2007
2009 app = wx.PyWidgetTester(size = (200, 300))
2010 edit_procedure(parent=app.frame)
2011
2012
2013 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2014
2015 gmI18N.activate_locale()
2016 gmI18N.install_domain()
2017 gmDateTime.init()
2018
2019
2020 pat = gmPerson.ask_for_patient()
2021 if pat is None:
2022 print "No patient. Exiting gracefully..."
2023 sys.exit(0)
2024 gmPatSearchWidgets.set_active_patient(patient=pat)
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042 test_edit_procedure()
2043
2044
2045