| Home | Trees | Indices | Help |
|
|---|
|
|
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 # stdlib
15 import sys, re, datetime as pydt, logging, time
16
17
18 # 3rd party
19 import wx
20 import wx.lib.pubsub as wxps
21
22
23 # GNUmed
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, gmPersonSearch
28 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
29 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
30 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl, wxgEncounterEditAreaDlg
31 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
32
33
34 _log = logging.getLogger('gm.ui')
35 _log.info(__version__)
36 #================================================================
37 # performed procedure related widgets/functions
38 #----------------------------------------------------------------
40
41 pat = gmPerson.gmCurrentPatient()
42 emr = pat.get_emr()
43
44 if parent is None:
45 parent = wx.GetApp().GetTopWindow()
46 #-----------------------------------------
47 def edit(procedure=None):
48 return edit_procedure(parent = parent, procedure = procedure)
49 #-----------------------------------------
50 def delete(procedure=None):
51 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
52 return True
53
54 gmDispatcher.send (
55 signal = u'statustext',
56 msg = _('Cannot delete performed procedure.'),
57 beep = True
58 )
59 return False
60 #-----------------------------------------
61 def refresh(lctrl):
62 procs = emr.get_performed_procedures()
63
64 items = [
65 [
66 p['clin_when'].strftime('%Y-%m-%d'),
67 p['clin_where'],
68 p['episode'],
69 p['performed_procedure']
70 ] for p in procs
71 ]
72 lctrl.set_string_items(items = items)
73 lctrl.set_data(data = procs)
74 #-----------------------------------------
75 gmListWidgets.get_choices_from_list (
76 parent = parent,
77 msg = _('\nSelect the procedure you want to edit !\n'),
78 caption = _('Editing performed procedures ...'),
79 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
80 single_selection = True,
81 edit_callback = edit,
82 new_callback = edit,
83 delete_callback = delete,
84 refresh_callback = refresh
85 )
86 #----------------------------------------------------------------
88 ea = cProcedureEAPnl(parent = parent, id = -1)
89 ea.data = procedure
90 ea.mode = gmTools.coalesce(procedure, 'new', 'edit')
91 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
92 dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure')))
93 if dlg.ShowModal() == wx.ID_OK:
94 dlg.Destroy()
95 return True
96 dlg.Destroy()
97 return False
98 #----------------------------------------------------------------
99 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
100
102
104 wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs)
105 gmEditArea.cGenericEditAreaMixin.__init__(self)
106
107 self.mode = 'new'
108 self.data = None
109
110 self.__init_ui()
111 #----------------------------------------------------------------
113 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
114 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
115
116 # location
117 mp = gmMatchProvider.cMatchProvider_SQL2 (
118 queries = [
119 u"""
120 select distinct on (clin_where) clin_where, clin_where
121 from clin.procedure
122 where clin_where %(fragment_condition)s
123 order by clin_where
124 limit 25
125 """ ]
126 )
127 mp.setThresholds(2, 4, 6)
128 self._PRW_location.matcher = mp
129
130 # procedure
131 mp = gmMatchProvider.cMatchProvider_SQL2 (
132 queries = [
133 u"""
134 select distinct on (narrative) narrative, narrative
135 from clin.procedure
136 where narrative %(fragment_condition)s
137 order by narrative
138 limit 25
139 """ ]
140 )
141 mp.setThresholds(2, 4, 6)
142 self._PRW_procedure.matcher = mp
143 #----------------------------------------------------------------
145 if self._PRW_hospital_stay.GetData() is None:
146 self._PRW_hospital_stay.SetText()
147 self._PRW_location.Enable(True)
148 self._PRW_episode.Enable(True)
149 else:
150 self._PRW_location.SetText()
151 self._PRW_location.Enable(False)
152 self._PRW_episode.SetText()
153 self._PRW_episode.Enable(False)
154 #----------------------------------------------------------------
156 if self._PRW_location.GetValue().strip() == u'':
157 self._PRW_hospital_stay.Enable(True)
158 # self._PRW_episode.Enable(False)
159 else:
160 self._PRW_hospital_stay.SetText()
161 self._PRW_hospital_stay.Enable(False)
162 # self._PRW_episode.Enable(True)
163 #----------------------------------------------------------------
164 # generic Edit Area mixin API
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 #----------------------------------------------------------------
212
213 pat = gmPerson.gmCurrentPatient()
214 emr = pat.get_emr()
215
216 if self._PRW_hospital_stay.GetData() is None:
217 stay = None
218 epi = self._PRW_episode.GetData()
219 loc = self._PRW_location.GetValue().strip()
220 else:
221 stay = self._PRW_hospital_stay.GetData()
222 epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode']
223 loc = None
224
225 proc = emr.add_performed_procedure (
226 episode = epi,
227 location = loc,
228 hospital_stay = stay,
229 procedure = self._PRW_procedure.GetValue().strip()
230 )
231 proc['clin_when'] = self._DPRW_date.data.get_pydt()
232 proc.save()
233
234 self.data = proc
235
236 return True
237 #----------------------------------------------------------------
239 self.data['clin_when'] = self._DPRW_date.data.get_pydt()
240
241 if self._PRW_hospital_stay.GetData() is None:
242 self.data['pk_hospital_stay'] = None
243 self.data['clin_where'] = self._PRW_location.GetValue().strip()
244 self.data['pk_episode'] = self._PRW_episode.GetData()
245 else:
246 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
247 self.data['clin_where'] = None
248 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
249 self.data['pk_episode'] = stay['pk_episode']
250
251 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
252
253 self.data.save()
254 return True
255 #----------------------------------------------------------------
257 self._DPRW_date.SetText()
258 self._PRW_hospital_stay.SetText()
259 self._PRW_location.SetText()
260 self._PRW_episode.SetText()
261 self._PRW_procedure.SetText()
262
263 self._DPRW_date.SetFocus()
264 #----------------------------------------------------------------
266 self._DPRW_date.SetData(data = self.data['clin_when'])
267 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
268 self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure'])
269
270 if self.data['pk_hospital_stay'] is None:
271 self._PRW_hospital_stay.SetText()
272 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
273 else:
274 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
275 self._PRW_location.SetText()
276
277 self._DPRW_date.SetFocus()
278 #----------------------------------------------------------------
280 self._refresh_as_new()
281 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
282 if self.data['pk_hospital_stay'] is None:
283 self._PRW_hospital_stay.SetText()
284 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
285 else:
286 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
287 self._PRW_location.SetText()
288
289 self._DPRW_date.SetFocus()
290 #----------------------------------------------------------------
294 #================================================================
295 # hospital stay related widgets/functions
296 #----------------------------------------------------------------
298
299 pat = gmPerson.gmCurrentPatient()
300 emr = pat.get_emr()
301
302 if parent is None:
303 parent = wx.GetApp().GetTopWindow()
304 #-----------------------------------------
305 def edit(stay=None):
306 return edit_hospital_stay(parent = parent, hospital_stay = stay)
307 #-----------------------------------------
308 def delete(stay=None):
309 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
310 return True
311 gmDispatcher.send (
312 signal = u'statustext',
313 msg = _('Cannot delete hospital stay.'),
314 beep = True
315 )
316 return False
317 #-----------------------------------------
318 def refresh(lctrl):
319 stays = emr.get_hospital_stays()
320 items = [
321 [
322 s['admission'].strftime('%Y-%m-%d'),
323 gmTools.coalesce(s['discharge'], u''),
324 s['episode'],
325 gmTools.coalesce(s['hospital'], u'')
326 ] for s in stays
327 ]
328 lctrl.set_string_items(items = items)
329 lctrl.set_data(data = stays)
330 #-----------------------------------------
331 gmListWidgets.get_choices_from_list (
332 parent = parent,
333 msg = _('\nSelect the hospital stay you want to edit !\n'),
334 caption = _('Editing hospital stays ...'),
335 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
336 single_selection = True,
337 edit_callback = edit,
338 new_callback = edit,
339 delete_callback = delete,
340 refresh_callback = refresh
341 )
342
343 #----------------------------------------------------------------
345 ea = cHospitalStayEditAreaPnl(parent = parent, id = -1)
346 ea.data = hospital_stay
347 ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit')
348 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
349 dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospital stay'), _('Editing a hospital stay')))
350 if dlg.ShowModal() == wx.ID_OK:
351 dlg.Destroy()
352 return True
353 dlg.Destroy()
354 return False
355 #----------------------------------------------------------------
357 """Phrasewheel to allow selection of a hospital stay.
358 """
360
361 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
362
363 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
364
365 mp = gmMatchProvider.cMatchProvider_SQL2 (
366 queries = [
367 u"""
368 select
369 pk_hospital_stay,
370 descr
371 from (
372 select distinct on (pk_hospital_stay)
373 pk_hospital_stay,
374 descr
375 from
376 (select
377 pk_hospital_stay,
378 (
379 to_char(admission, 'YYYY-Mon-DD')
380 || coalesce((' (' || hospital || '):'), ': ')
381 || episode
382 || coalesce((' (' || health_issue || ')'), '')
383 ) as descr
384 from
385 clin.v_pat_hospital_stays
386 where
387 %(ctxt_pat)s
388
389 hospital %(fragment_condition)s
390 or
391 episode %(fragment_condition)s
392 or
393 health_issue %(fragment_condition)s
394 ) as the_stays
395 ) as distinct_stays
396 order by descr
397 limit 25
398 """ ],
399 context = ctxt
400 )
401 mp.setThresholds(3, 4, 6)
402 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
403
404 self.matcher = mp
405 self.selection_only = True
406 #----------------------------------------------------------------
407 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
408
409 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
410
412 wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl.__init__(self, *args, **kwargs)
413 gmEditArea.cGenericEditAreaMixin.__init__(self)
414 #----------------------------------------------------------------
415 # generic Edit Area mixin API
416 #----------------------------------------------------------------
418 if not self._DP_admission.GetValue().IsValid():
419 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
420 wxps.Publisher().sendMessage (
421 topic = 'statustext',
422 data = {'msg': _('Missing admission data. Cannot save hospital stay.'), 'beep': True}
423 )
424 return False
425 else:
426 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
427
428 if self._DP_discharge.GetValue().IsValid():
429 if not self._DP_discharge.GetValue().IsLaterThan(self._DP_admission.GetValue()):
430 self._DP_discharge.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
431 wxps.Publisher().sendMessage (
432 topic = 'statustext',
433 data = {'msg': _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), 'beep': True}
434 )
435 return False
436
437 if self._PRW_episode.GetValue().strip() == u'':
438 self._PRW_episode.display_as_valid(False)
439 wxps.Publisher().sendMessage (
440 topic = 'statustext',
441 data = {'msg': _('Must select an episode or enter a name for a new one. Cannot save hospital stay.'), 'beep': True}
442 )
443 return False
444
445 return True
446 #----------------------------------------------------------------
448
449 pat = gmPerson.gmCurrentPatient()
450 emr = pat.get_emr()
451
452 stay = gmEMRStructItems.create_hospital_stay (
453 encounter = emr.active_encounter['pk_encounter'],
454 episode = self._PRW_episode.GetData(can_create = True)
455 )
456 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
457 stay['admission'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue())
458 if self._DP_discharge.GetValue().IsValid():
459 stay['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue())
460 stay.save_payload()
461
462 self.data = stay
463 return True
464 #----------------------------------------------------------------
466
467 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
468 self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
469 self.data['admission'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue())
470 if self._DP_discharge.GetValue().IsValid():
471 self.data['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue())
472 self.data.save_payload()
473
474 return True
475 #----------------------------------------------------------------
477 self._PRW_hospital.SetText(value = u'')
478 self._PRW_episode.SetText(value = u'')
479 self._DP_admission.SetValue(dt = wx.DateTime.UNow())
480 #self._DP_discharge.SetValue(dt = None)
481 #----------------------------------------------------------------
483 if self.data['hospital'] is not None:
484 self._PRW_hospital.SetText(value = self.data['hospital'])
485
486 if self.data['pk_episode'] is not None:
487 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
488
489 self._DP_admission.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['admission'], wx = wx))
490
491 if self.data['discharge'] is not None:
492 self._DP_discharge.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['discharge'], wx = wx))
493 #----------------------------------------------------------------
496 #================================================================
497 # encounter related widgets/functions
498 #----------------------------------------------------------------
500 emr.start_new_encounter()
501 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True)
502 time.sleep(0.5)
503 gmGuiHelpers.gm_show_info (
504 _('\nA new encounter was started for the active patient.\n'),
505 _('Start of new encounter')
506 )
507 #----------------------------------------------------------------
509
510 if parent is None:
511 parent = wx.GetApp().GetTopWindow()
512
513 # FIXME: use generic dialog 2
514 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
515 dlg.ShowModal()
516 #----------------------------------------------------------------
518
519 if patient is None:
520 patient = gmPerson.gmCurrentPatient()
521
522 if not patient.connected:
523 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
524 return False
525
526 if parent is None:
527 parent = wx.GetApp().GetTopWindow()
528
529 emr = patient.get_emr()
530
531 #--------------------
532 def refresh(lctrl):
533 if encounters is not None:
534 encs = encounters
535 else:
536 encs = emr.get_encounters()
537
538 items = [
539 [
540 e['started'].strftime('%x %H:%M'),
541 e['last_affirmed'].strftime('%H:%M'),
542 e['l10n_type'],
543 gmTools.coalesce(e['reason_for_encounter'], u''),
544 gmTools.coalesce(e['assessment_of_encounter'], u''),
545 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
546 e['pk_encounter']
547 ] for e in encs
548 ]
549
550 lctrl.set_string_items(items = items)
551 lctrl.set_data(data = encs)
552 #--------------------
553 def edit(enc = None):
554 return edit_encounter(parent = parent, encounter = enc)
555 #--------------------
556 return gmListWidgets.get_choices_from_list (
557 parent = parent,
558 msg = _('\nBelow find the relevant encounters of the patient.\n'),
559 caption = _('Encounters ...'),
560 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
561 can_return_empty = True,
562 single_selection = single_selection,
563 refresh_callback = refresh,
564 edit_callback = edit
565 )
566 #----------------------------------------------------------------
568 """This is used as the callback when the EMR detects that the
569 patient was here rather recently and wants to ask the
570 provider whether to continue the recent encounter.
571 """
572 if parent is None:
573 parent = wx.GetApp().GetTopWindow()
574
575 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
576 parent = None,
577 id = -1,
578 caption = caption,
579 question = msg,
580 button_defs = [
581 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
582 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
583 ],
584 show_checkbox = False
585 )
586
587 result = dlg.ShowModal()
588 dlg.Destroy()
589
590 if result == wx.ID_YES:
591 return True
592
593 return False
594 #----------------------------------------------------------------
596
597 if parent is None:
598 parent = wx.GetApp().GetTopWindow()
599
600 #--------------------
601 def edit(enc_type=None):
602 return edit_encounter_type(parent = parent, encounter_type = enc_type)
603 #--------------------
604 def delete(enc_type=None):
605 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
606 return True
607 gmDispatcher.send (
608 signal = u'statustext',
609 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
610 beep = True
611 )
612 return False
613 #--------------------
614 def refresh(lctrl):
615 enc_types = gmEMRStructItems.get_encounter_types()
616 lctrl.set_string_items(items = enc_types)
617 #--------------------
618 gmListWidgets.get_choices_from_list (
619 parent = parent,
620 msg = _('\nSelect the encounter type you want to edit !\n'),
621 caption = _('Managing encounter types ...'),
622 columns = [_('Local name'), _('Encounter type')],
623 single_selection = True,
624 edit_callback = edit,
625 new_callback = edit,
626 delete_callback = delete,
627 refresh_callback = refresh
628 )
629 #----------------------------------------------------------------
631 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1)
632 ea.data = encounter_type
633 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit')
634 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
635 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name')))
636 if dlg.ShowModal() == wx.ID_OK:
637 return True
638 return False
639 #----------------------------------------------------------------
641 """Phrasewheel to allow selection of encounter type.
642
643 - user input interpreted as encounter type in English or local language
644 - data returned is pk of corresponding encounter type or None
645 """
647
648 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
649
650 mp = gmMatchProvider.cMatchProvider_SQL2 (
651 queries = [
652 u"""
653 select pk, l10n_description from (
654 select distinct on (pk) * from (
655 (select
656 pk,
657 _(description) as l10n_description,
658 1 as rank
659 from
660 clin.encounter_type
661 where
662 _(description) %(fragment_condition)s
663
664 ) union all (
665
666 select
667 pk,
668 _(description) as l10n_description,
669 2 as rank
670 from
671 clin.encounter_type
672 where
673 description %(fragment_condition)s
674 )
675 ) as q_distinct_pk
676 ) as q_ordered order by rank, l10n_description
677 """ ]
678 )
679 mp.setThresholds(2, 4, 6)
680
681 self.matcher = mp
682 self.selection_only = True
683 self.picklist_delay = 50
684 #----------------------------------------------------------------
685 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
686
688
689 wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl.__init__(self, *args, **kwargs)
690 gmEditArea.cGenericEditAreaMixin.__init__(self)
691
692 # self.__register_interests()
693 #-------------------------------------------------------
694 # generic edit area API
695 #-------------------------------------------------------
697 if self.mode == 'edit':
698 if self._TCTRL_l10n_name.GetValue().strip() == u'':
699 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
700 return False
701 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
702 return True
703
704 no_errors = True
705
706 if self._TCTRL_l10n_name.GetValue().strip() == u'':
707 if self._TCTRL_name.GetValue().strip() == u'':
708 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
709 no_errors = False
710 else:
711 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
712 else:
713 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
714
715 if self._TCTRL_name.GetValue().strip() == u'':
716 if self._TCTRL_l10n_name.GetValue().strip() == u'':
717 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False)
718 no_errors = False
719 else:
720 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
721 else:
722 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
723
724 return no_errors
725 #-------------------------------------------------------
727 enc_type = gmEMRStructItems.create_encounter_type (
728 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''),
729 l10n_description = gmTools.coalesce (
730 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''),
731 self._TCTRL_name.GetValue().strip()
732 )
733 )
734 if enc_type is None:
735 return False
736 self.data = enc_type
737 return True
738 #-------------------------------------------------------
740 enc_type = gmEMRStructItems.update_encounter_type (
741 description = self._TCTRL_name.GetValue().strip(),
742 l10n_description = self._TCTRL_l10n_name.GetValue().strip()
743 )
744 if enc_type is None:
745 return False
746 self.data = enc_type
747 return True
748 #-------------------------------------------------------
750 self._TCTRL_l10n_name.SetValue(u'')
751 self._TCTRL_name.SetValue(u'')
752 self._TCTRL_name.Enable(True)
753 #-------------------------------------------------------
755 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
756 self._TCTRL_name.SetValue(self.data['description'])
757 # disallow changing type on all encounters by editing system name
758 self._TCTRL_name.Enable(False)
759 #-------------------------------------------------------
764 #-------------------------------------------------------
765 # internal API
766 #-------------------------------------------------------
767 # def __register_interests(self):
768 # return
769 #----------------------------------------------------------------
771
773 try:
774 self.__encounter = kwargs['encounter']
775 del kwargs['encounter']
776 except KeyError:
777 self.__encounter = None
778
779 try:
780 msg = kwargs['msg']
781 del kwargs['msg']
782 except KeyError:
783 msg = None
784
785 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
786
787 self.refresh(msg = msg)
788 #--------------------------------------------------------
789 # external API
790 #--------------------------------------------------------
792
793 if msg is not None:
794 self._LBL_instructions.SetLabel(msg)
795
796 if encounter is not None:
797 self.__encounter = encounter
798
799 if self.__encounter is None:
800 return True
801
802 # getting the patient via the encounter allows us to act
803 # on any encounter regardless of the currently active patient
804 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
805 self._LBL_patient.SetLabel(pat.get_description_gender())
806
807 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
808
809 fts = gmDateTime.cFuzzyTimestamp (
810 timestamp = self.__encounter['started'],
811 accuracy = gmDateTime.acc_minutes
812 )
813 self._PRW_start.SetText(fts.format_accurately(), data=fts)
814
815 fts = gmDateTime.cFuzzyTimestamp (
816 timestamp = self.__encounter['last_affirmed'],
817 accuracy = gmDateTime.acc_minutes
818 )
819 self._PRW_end.SetText(fts.format_accurately(), data=fts)
820
821 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
822 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
823
824 if self.__encounter['last_affirmed'] == self.__encounter['started']:
825 self._PRW_end.SetFocus()
826 else:
827 self._TCTRL_aoe.SetFocus()
828
829 return True
830 #--------------------------------------------------------
832
833 if self._PRW_encounter_type.GetData() is None:
834 self._PRW_encounter_type.SetBackgroundColour('pink')
835 self._PRW_encounter_type.Refresh()
836 self._PRW_encounter_type.SetFocus()
837 return False
838 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
839 self._PRW_encounter_type.Refresh()
840
841 if not self._PRW_start.is_valid_timestamp():
842 self._PRW_start.SetFocus()
843 return False
844
845 if not self._PRW_end.is_valid_timestamp():
846 self._PRW_end.SetFocus()
847 return False
848
849 return True
850 #--------------------------------------------------------
852 if not self.__is_valid_for_save():
853 return False
854
855 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
856 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
857 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
858 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
859 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
860 self.__encounter.save_payload() # FIXME: error checking
861
862 return True
863 #----------------------------------------------------------------
864 # FIXME: use generic dialog 2
866
868 encounter = kwargs['encounter']
869 del kwargs['encounter']
870
871 try:
872 button_defs = kwargs['button_defs']
873 del kwargs['button_defs']
874 except KeyError:
875 button_defs = None
876
877 try:
878 msg = kwargs['msg']
879 del kwargs['msg']
880 except KeyError:
881 msg = None
882
883 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
884 self.SetSize((450, 280))
885 self.SetMinSize((450, 280))
886
887 if button_defs is not None:
888 self._BTN_save.SetLabel(button_defs[0][0])
889 self._BTN_save.SetToolTipString(button_defs[0][1])
890 self._BTN_close.SetLabel(button_defs[1][0])
891 self._BTN_close.SetToolTipString(button_defs[1][1])
892 self.Refresh()
893
894 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
895
896 self.Fit()
897 #--------------------------------------------------------
904 #================================================================
905 # episode related widgets/functions
906 #----------------------------------------------------------------
908 ea = cEpisodeEditAreaPnl(parent = parent, id = -1)
909 ea.data = episode
910 ea.mode = gmTools.coalesce(episode, 'new', 'edit')
911 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
912 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode')))
913 if dlg.ShowModal() == wx.ID_OK:
914 return True
915 return False
916 #----------------------------------------------------------------
918
919 created_new_issue = False
920
921 try:
922 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient'])
923 except gmExceptions.NoSuchBusinessObjectError:
924 issue = None
925
926 if issue is None:
927 issue = emr.add_health_issue(issue_name = episode['description'])
928 created_new_issue = True
929 else:
930 # issue exists already, so ask user
931 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
932 parent,
933 -1,
934 caption = _('Promoting episode to health issue'),
935 question = _(
936 'There already is a health issue\n'
937 '\n'
938 ' %s\n'
939 '\n'
940 'What do you want to do ?'
941 ) % issue['description'],
942 button_defs = [
943 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False},
944 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True}
945 ]
946 )
947 use_existing = dlg.ShowModal()
948 dlg.Destroy()
949
950 if use_existing == wx.ID_CANCEL:
951 return
952
953 # user wants to create new issue with alternate name
954 if use_existing == wx.ID_NO:
955 # loop until name modified but non-empty or cancelled
956 issue_name = episode['description']
957 while issue_name == episode['description']:
958 dlg = wx.TextEntryDialog (
959 parent = parent,
960 message = _('Enter a short descriptive name for the new health issue:'),
961 caption = _('Creating a new health issue ...'),
962 defaultValue = issue_name,
963 style = wx.OK | wx.CANCEL | wx.CENTRE
964 )
965 decision = dlg.ShowModal()
966 if decision != wx.ID_OK:
967 dlg.Destroy()
968 return
969 issue_name = dlg.GetValue().strip()
970 dlg.Destroy()
971 if issue_name == u'':
972 issue_name = episode['description']
973
974 issue = emr.add_health_issue(issue_name = issue_name)
975 created_new_issue = True
976
977 # eventually move the episode to the issue
978 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True):
979 # user cancelled the move so delete just-created issue
980 if created_new_issue:
981 # shouldn't fail as it is completely new
982 gmEMRStructItems.delete_health_issue(health_issue = issue)
983 return
984
985 return
986 #----------------------------------------------------------------
988 """Prepare changing health issue for an episode.
989
990 Checks for two-open-episodes conflict. When this
991 function succeeds, the pk_health_issue has been set
992 on the episode instance and the episode should - for
993 all practical purposes - be ready for save_payload().
994 """
995 # episode is closed: should always work
996 if not episode['episode_open']:
997 episode['pk_health_issue'] = target_issue['pk_health_issue']
998 if save_to_backend:
999 episode.save_payload()
1000 return True
1001
1002 # un-associate: should always work, too
1003 if target_issue is None:
1004 episode['pk_health_issue'] = None
1005 if save_to_backend:
1006 episode.save_payload()
1007 return True
1008
1009 # try closing possibly expired episode on target issue if any
1010 db_cfg = gmCfg.cCfgSQL()
1011 epi_ttl = int(db_cfg.get2 (
1012 option = u'episode.ttl',
1013 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1014 bias = 'user',
1015 default = 60 # 2 months
1016 ))
1017 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1018 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1019 existing_epi = target_issue.get_open_episode()
1020
1021 # no more open episode on target issue: should work now
1022 if existing_epi is None:
1023 episode['pk_health_issue'] = target_issue['pk_health_issue']
1024 if save_to_backend:
1025 episode.save_payload()
1026 return True
1027
1028 # don't conflict on SELF ;-)
1029 if existing_epi['pk_episode'] == episode['pk_episode']:
1030 episode['pk_health_issue'] = target_issue['pk_health_issue']
1031 if save_to_backend:
1032 episode.save_payload()
1033 return True
1034
1035 # we got two open episodes at once, ask user
1036 move_range = episode.get_access_range()
1037 exist_range = existing_epi.get_access_range()
1038 question = _(
1039 'You want to associate the running episode:\n\n'
1040 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1041 'with the health issue:\n\n'
1042 ' "%(issue_name)s"\n\n'
1043 'There already is another episode running\n'
1044 'for this health issue:\n\n'
1045 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1046 'However, there can only be one running\n'
1047 'episode per health issue.\n\n'
1048 'Which episode do you want to close ?'
1049 ) % {
1050 'new_epi_name': episode['description'],
1051 'new_epi_start': move_range[0].strftime('%m/%y'),
1052 'new_epi_end': move_range[1].strftime('%m/%y'),
1053 'issue_name': target_issue['description'],
1054 'old_epi_name': existing_epi['description'],
1055 'old_epi_start': exist_range[0].strftime('%m/%y'),
1056 'old_epi_end': exist_range[1].strftime('%m/%y')
1057 }
1058 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1059 parent = None,
1060 id = -1,
1061 caption = _('Resolving two-running-episodes conflict'),
1062 question = question,
1063 button_defs = [
1064 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1065 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1066 ]
1067 )
1068 decision = dlg.ShowModal()
1069
1070 if decision == wx.ID_CANCEL:
1071 # button 3: move cancelled by user
1072 return False
1073
1074 elif decision == wx.ID_YES:
1075 # button 1: close old episode
1076 existing_epi['episode_open'] = False
1077 existing_epi.save_payload()
1078
1079 elif decision == wx.ID_NO:
1080 # button 2: close new episode
1081 episode['episode_open'] = False
1082
1083 else:
1084 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1085
1086 episode['pk_health_issue'] = target_issue['pk_health_issue']
1087 if save_to_backend:
1088 episode.save_payload()
1089 return True
1090 #----------------------------------------------------------------
1092
1093 # FIXME: support pre-selection
1094
1096
1097 episodes = kwargs['episodes']
1098 del kwargs['episodes']
1099
1100 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1101
1102 self.SetTitle(_('Select the episodes you are interested in ...'))
1103 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')])
1104 self._LCTRL_items.set_string_items (
1105 items = [
1106 [ epi['description'],
1107 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''),
1108 gmTools.coalesce(epi['health_issue'], u'')
1109 ]
1110 for epi in episodes ]
1111 )
1112 self._LCTRL_items.set_column_widths()
1113 self._LCTRL_items.set_data(data = episodes)
1114 #----------------------------------------------------------------
1116 """Let user select an episode *description*.
1117
1118 The user can select an episode description from the previously
1119 used descriptions across all episodes across all patients.
1120
1121 Selection is done with a phrasewheel so the user can
1122 type the episode name and matches will be shown. Typing
1123 "*" will show the entire list of episodes.
1124
1125 If the user types a description not existing yet a
1126 new episode description will be returned.
1127 """
1129
1130 mp = gmMatchProvider.cMatchProvider_SQL2 (
1131 queries = [u"""
1132 select distinct on (description) description, description, 1
1133 from clin.episode
1134 where description %(fragment_condition)s
1135 order by description
1136 limit 30"""
1137 ]
1138 )
1139 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1140 self.matcher = mp
1141 #----------------------------------------------------------------
1143 """Let user select an episode.
1144
1145 The user can select an episode from the existing episodes of a
1146 patient. Selection is done with a phrasewheel so the user
1147 can type the episode name and matches will be shown. Typing
1148 "*" will show the entire list of episodes. Closed episodes
1149 will be marked as such. If the user types an episode name not
1150 in the list of existing episodes a new episode can be created
1151 from it if the programmer activated that feature.
1152
1153 If keyword <patient_id> is set to None or left out the control
1154 will listen to patient change signals and therefore act on
1155 gmPerson.gmCurrentPatient() changes.
1156 """
1158
1159 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1160
1161 mp = gmMatchProvider.cMatchProvider_SQL2 (
1162 queries = [
1163 u"""(
1164
1165 select
1166 pk_episode,
1167 coalesce (
1168 description || ' - ' || health_issue,
1169 description
1170 ) as description,
1171 1 as rank
1172 from
1173 clin.v_pat_episodes
1174 where
1175 episode_open is true and
1176 description %(fragment_condition)s
1177 %(ctxt_pat)s
1178
1179 ) union all (
1180
1181 select
1182 pk_episode,
1183 coalesce (
1184 description || _(' (closed)') || ' - ' || health_issue,
1185 description || _(' (closed)')
1186 ) as description,
1187 2 as rank
1188 from
1189 clin.v_pat_episodes
1190 where
1191 description %(fragment_condition)s and
1192 episode_open is false
1193 %(ctxt_pat)s
1194
1195 )
1196 order by rank, description
1197 limit 30"""
1198 ],
1199 context = ctxt
1200 )
1201
1202 try:
1203 kwargs['patient_id']
1204 except KeyError:
1205 kwargs['patient_id'] = None
1206
1207 if kwargs['patient_id'] is None:
1208 self.use_current_patient = True
1209 self.__register_patient_change_signals()
1210 pat = gmPerson.gmCurrentPatient()
1211 if pat.connected:
1212 mp.set_context('pat', pat.ID)
1213 else:
1214 self.use_current_patient = False
1215 self.__patient_id = int(kwargs['patient_id'])
1216 mp.set_context('pat', self.__patient_id)
1217
1218 del kwargs['patient_id']
1219
1220 gmPhraseWheel.cPhraseWheel.__init__ (
1221 self,
1222 *args,
1223 **kwargs
1224 )
1225 self.matcher = mp
1226 #--------------------------------------------------------
1227 # external API
1228 #--------------------------------------------------------
1230 if self.use_current_patient:
1231 return False
1232 self.__patient_id = int(patient_id)
1233 self.set_context('pat', self.__patient_id)
1234 return True
1235 #--------------------------------------------------------
1237 self.__is_open_for_create_data = is_open # used (only) in _create_data()
1238 gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1239 return self.data
1240 #--------------------------------------------------------
1242
1243 epi_name = self.GetValue().strip()
1244 if epi_name == u'':
1245 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1246 _log.debug('cannot create episode without name')
1247 return
1248
1249 if self.use_current_patient:
1250 pat = gmPerson.gmCurrentPatient()
1251 else:
1252 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1253
1254 emr = pat.get_emr()
1255 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1256 if epi is None:
1257 self.data = None
1258 else:
1259 self.data = epi['pk_episode']
1260 #--------------------------------------------------------
1263 #--------------------------------------------------------
1264 # internal API
1265 #--------------------------------------------------------
1267 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1268 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1269 #--------------------------------------------------------
1272 #--------------------------------------------------------
1274 if self.use_current_patient:
1275 patient = gmPerson.gmCurrentPatient()
1276 self.set_context('pat', patient.ID)
1277 return True
1278 #----------------------------------------------------------------
1279 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1280
1281 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1282
1284
1285 try:
1286 episode = kwargs['episode']
1287 del kwargs['episode']
1288 except KeyError:
1289 episode = None
1290
1291 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
1292 gmEditArea.cGenericEditAreaMixin.__init__(self)
1293
1294 self.data = episode
1295 #----------------------------------------------------------------
1296 # generic Edit Area mixin API
1297 #----------------------------------------------------------------
1299
1300 errors = False
1301
1302 if len(self._PRW_description.GetValue().strip()) == 0:
1303 errors = True
1304 self._PRW_description.display_as_valid(False)
1305 self._PRW_description.SetFocus()
1306 else:
1307 self._PRW_description.display_as_valid(True)
1308 self._PRW_description.Refresh()
1309
1310 return not errors
1311 #----------------------------------------------------------------
1313
1314 pat = gmPerson.gmCurrentPatient()
1315 emr = pat.get_emr()
1316
1317 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1318 epi['summary'] = self._TCTRL_summary.GetValue().strip()
1319 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1320 epi['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1321
1322 issue_name = self._PRW_issue.GetValue().strip()
1323 if len(issue_name) != 0:
1324 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1325 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1326
1327 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1328 gmDispatcher.send (
1329 signal = 'statustext',
1330 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1331 epi['description'],
1332 issue['description']
1333 )
1334 )
1335 gmEMRStructItems.delete_episode(episode = epi)
1336 return False
1337
1338 epi.save()
1339
1340 self.data = epi
1341 return True
1342 #----------------------------------------------------------------
1344
1345 self.data['description'] = self._PRW_description.GetValue().strip()
1346 self.data['summary'] = self._TCTRL_summary.GetValue().strip()
1347 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1348 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1349
1350 issue_name = self._PRW_issue.GetValue().strip()
1351 if len(issue_name) != 0:
1352 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1353 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1354
1355 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1356 gmDispatcher.send (
1357 signal = 'statustext',
1358 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1359 self.data['description'],
1360 issue['description']
1361 )
1362 )
1363 return False
1364
1365 self.data.save()
1366 return True
1367 #----------------------------------------------------------------
1369 if self.data is None:
1370 ident = gmPerson.gmCurrentPatient()
1371 else:
1372 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1373 self._TCTRL_patient.SetValue(ident.get_description_gender())
1374 self._PRW_issue.SetText()
1375 self._PRW_description.SetText()
1376 self._TCTRL_summary.SetValue(u'')
1377 self._PRW_classification.SetText()
1378 self._CHBOX_closed.SetValue(False)
1379 #----------------------------------------------------------------
1381 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1382 self._TCTRL_patient.SetValue(ident.get_description_gender())
1383
1384 if self.data['pk_health_issue'] is not None:
1385 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue'])
1386
1387 self._PRW_description.SetText(self.data['description'], data=self.data['description'])
1388
1389 self._TCTRL_summary.SetValue(gmTools.coalesce(self.data['summary'], u''))
1390
1391 if self.data['diagnostic_certainty_classification'] is not None:
1392 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification'])
1393
1394 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1395 #----------------------------------------------------------------
1398 #================================================================
1399 # health issue related widgets/functions
1400 #----------------------------------------------------------------
1402 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1)
1403 ea.data = issue
1404 ea.mode = gmTools.coalesce(issue, 'new', 'edit')
1405 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None))
1406 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue')))
1407 if dlg.ShowModal() == wx.ID_OK:
1408 return True
1409 return False
1410 #----------------------------------------------------------------
1412
1413 # FIXME: support pre-selection
1414
1416
1417 issues = kwargs['issues']
1418 del kwargs['issues']
1419
1420 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1421
1422 self.SetTitle(_('Select the health issues you are interested in ...'))
1423 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1424
1425 for issue in issues:
1426 if issue['is_confidential']:
1427 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1428 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1429 else:
1430 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1431
1432 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1433 if issue['clinically_relevant']:
1434 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1435 if issue['is_active']:
1436 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1437 if issue['is_cause_of_death']:
1438 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1439
1440 self._LCTRL_items.set_column_widths()
1441 self._LCTRL_items.set_data(data = issues)
1442 #----------------------------------------------------------------
1444 """Let the user select a health issue.
1445
1446 The user can select a health issue from the existing issues
1447 of a patient. Selection is done with a phrasewheel so the user
1448 can type the issue name and matches will be shown. Typing
1449 "*" will show the entire list of issues. Inactive issues
1450 will be marked as such. If the user types an issue name not
1451 in the list of existing issues a new issue can be created
1452 from it if the programmer activated that feature.
1453
1454 If keyword <patient_id> is set to None or left out the control
1455 will listen to patient change signals and therefore act on
1456 gmPerson.gmCurrentPatient() changes.
1457 """
1459
1460 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1461
1462 mp = gmMatchProvider.cMatchProvider_SQL2 (
1463 # FIXME: consider clin.health_issue.clinically_relevant
1464 queries = [u"""
1465 (select pk_health_issue, description, 1
1466 from clin.v_health_issues where
1467 is_active is true and
1468 description %(fragment_condition)s and
1469 %(ctxt_pat)s
1470 order by description)
1471
1472 union
1473
1474 (select pk_health_issue, description || _(' (inactive)'), 2
1475 from clin.v_health_issues where
1476 is_active is false and
1477 description %(fragment_condition)s and
1478 %(ctxt_pat)s
1479 order by description)"""
1480 ],
1481 context = ctxt
1482 )
1483
1484 try: kwargs['patient_id']
1485 except KeyError: kwargs['patient_id'] = None
1486
1487 if kwargs['patient_id'] is None:
1488 self.use_current_patient = True
1489 self.__register_patient_change_signals()
1490 pat = gmPerson.gmCurrentPatient()
1491 if pat.connected:
1492 mp.set_context('pat', pat.ID)
1493 else:
1494 self.use_current_patient = False
1495 self.__patient_id = int(kwargs['patient_id'])
1496 mp.set_context('pat', self.__patient_id)
1497
1498 del kwargs['patient_id']
1499
1500 gmPhraseWheel.cPhraseWheel.__init__ (
1501 self,
1502 *args,
1503 **kwargs
1504 )
1505 self.matcher = mp
1506 #--------------------------------------------------------
1507 # external API
1508 #--------------------------------------------------------
1510 if self.use_current_patient:
1511 return False
1512 self.__patient_id = int(patient_id)
1513 self.set_context('pat', self.__patient_id)
1514 return True
1515 #--------------------------------------------------------
1517 if self.data is None:
1518 if can_create:
1519 issue_name = self.GetValue().strip()
1520
1521 if self.use_current_patient:
1522 pat = gmPerson.gmCurrentPatient()
1523 else:
1524 pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
1525 emr = pat.get_emr()
1526
1527 issue = emr.add_health_issue(issue_name = issue_name)
1528 if issue is None:
1529 self.data = None
1530 else:
1531 self.data = issue['pk_health_issue']
1532
1533 return gmPhraseWheel.cPhraseWheel.GetData(self)
1534 #--------------------------------------------------------
1535 # internal API
1536 #--------------------------------------------------------
1538 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1539 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1540 #--------------------------------------------------------
1543 #--------------------------------------------------------
1545 if self.use_current_patient:
1546 patient = gmPerson.gmCurrentPatient()
1547 self.set_context('pat', patient.ID)
1548 return True
1549 #------------------------------------------------------------
1551
1553 try:
1554 msg = kwargs['message']
1555 except KeyError:
1556 msg = None
1557 del kwargs['message']
1558 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1559 if msg is not None:
1560 self._lbl_message.SetLabel(label=msg)
1561 #--------------------------------------------------------
1572 #------------------------------------------------------------
1573 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1574
1575 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1576 """Panel encapsulating health issue edit area functionality."""
1577
1579
1580 try:
1581 issue = kwargs['issue']
1582 except KeyError:
1583 issue = None
1584
1585 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1586
1587 gmEditArea.cGenericEditAreaMixin.__init__(self)
1588
1589 # FIXME: include more sources: coding systems/other database columns
1590 mp = gmMatchProvider.cMatchProvider_SQL2 (
1591 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1592 )
1593 mp.setThresholds(1, 3, 5)
1594 self._PRW_condition.matcher = mp
1595
1596 mp = gmMatchProvider.cMatchProvider_SQL2 (
1597 queries = [u"""
1598 select distinct on (grouping) grouping, grouping from (
1599
1600 select rank, grouping from ((
1601
1602 select
1603 grouping,
1604 1 as rank
1605 from
1606 clin.health_issue
1607 where
1608 grouping %%(fragment_condition)s
1609 and
1610 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1611
1612 ) union (
1613
1614 select
1615 grouping,
1616 2 as rank
1617 from
1618 clin.health_issue
1619 where
1620 grouping %%(fragment_condition)s
1621
1622 )) as union_result
1623
1624 order by rank
1625
1626 ) as order_result
1627
1628 limit 50""" % gmPerson.gmCurrentPatient().ID
1629 ]
1630 )
1631 mp.setThresholds(1, 3, 5)
1632 self._PRW_grouping.matcher = mp
1633
1634 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1635 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1636
1637 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1638 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1639
1640 self._PRW_year_noted.Enable(True)
1641
1642 self.data = issue
1643 #----------------------------------------------------------------
1644 # generic Edit Area mixin API
1645 #----------------------------------------------------------------
1647
1648 if self._PRW_condition.GetValue().strip() == '':
1649 self._PRW_condition.display_as_valid(False)
1650 self._PRW_condition.SetFocus()
1651 return False
1652 self._PRW_condition.display_as_valid(True)
1653 self._PRW_condition.Refresh()
1654
1655 # FIXME: sanity check age/year diagnosed
1656 age_noted = self._PRW_age_noted.GetValue().strip()
1657 if age_noted != '':
1658 if gmDateTime.str2interval(str_interval = age_noted) is None:
1659 self._PRW_age_noted.display_as_valid(False)
1660 self._PRW_age_noted.SetFocus()
1661 return False
1662 self._PRW_age_noted.display_as_valid(True)
1663
1664 return True
1665 #----------------------------------------------------------------
1667 pat = gmPerson.gmCurrentPatient()
1668 emr = pat.get_emr()
1669
1670 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1671
1672 side = u''
1673 if self._ChBOX_left.GetValue():
1674 side += u's'
1675 if self._ChBOX_right.GetValue():
1676 side += u'd'
1677 issue['laterality'] = side
1678
1679 issue['summary'] = self._TCTRL_summary.GetValue().strip()
1680 issue['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1681 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1682 issue['is_active'] = self._ChBOX_active.GetValue()
1683 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1684 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
1685 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
1686
1687 age_noted = self._PRW_age_noted.GetData()
1688 if age_noted is not None:
1689 issue['age_noted'] = age_noted
1690
1691 issue.save()
1692
1693 self.data = issue
1694 return True
1695 #----------------------------------------------------------------
1697
1698 self.data['description'] = self._PRW_condition.GetValue().strip()
1699
1700 side = u''
1701 if self._ChBOX_left.GetValue():
1702 side += u's'
1703 if self._ChBOX_right.GetValue():
1704 side += u'd'
1705 self.data['laterality'] = side
1706
1707 self.data['summary'] = self._TCTRL_summary.GetValue().strip()
1708 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData()
1709 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
1710 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
1711 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
1712 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
1713 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
1714
1715 age_noted = self._PRW_age_noted.GetData()
1716 if age_noted is not None:
1717 self.data['age_noted'] = age_noted
1718
1719 self.data.save()
1720
1721 # FIXME: handle is_operation
1722 return True
1723 #----------------------------------------------------------------
1725 self._PRW_condition.SetText()
1726 self._ChBOX_left.SetValue(0)
1727 self._ChBOX_right.SetValue(0)
1728 self._PRW_classification.SetText()
1729 self._PRW_grouping.SetText()
1730 self._TCTRL_summary.SetValue(u'')
1731 self._PRW_age_noted.SetText()
1732 self._PRW_year_noted.SetText()
1733 self._ChBOX_active.SetValue(0)
1734 self._ChBOX_relevant.SetValue(1)
1735 self._ChBOX_is_operation.SetValue(0)
1736 self._ChBOX_confidential.SetValue(0)
1737 self._ChBOX_caused_death.SetValue(0)
1738
1739 return True
1740 #----------------------------------------------------------------
1742 self._PRW_condition.SetText(self.data['description'])
1743
1744 lat = gmTools.coalesce(self.data['laterality'], '')
1745 if lat.find('s') == -1:
1746 self._ChBOX_left.SetValue(0)
1747 else:
1748 self._ChBOX_left.SetValue(1)
1749 if lat.find('d') == -1:
1750 self._ChBOX_right.SetValue(0)
1751 else:
1752 self._ChBOX_right.SetValue(1)
1753
1754 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification'])
1755 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
1756 self._TCTRL_summary.SetValue(gmTools.coalesce(self.data['summary'], u''))
1757
1758 if self.data['age_noted'] is None:
1759 self._PRW_age_noted.SetText()
1760 else:
1761 self._PRW_age_noted.SetText (
1762 value = '%sd' % self.data['age_noted'].days,
1763 data = self.data['age_noted']
1764 )
1765
1766 self._ChBOX_active.SetValue(self.data['is_active'])
1767 self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
1768 self._ChBOX_is_operation.SetValue(0) # FIXME
1769 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
1770 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
1771
1772 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ...
1773 # self._PRW_age_noted.SetFocus()
1774 # self._PRW_condition.SetFocus()
1775
1776 return True
1777 #----------------------------------------------------------------
1780 #--------------------------------------------------------
1781 # internal helpers
1782 #--------------------------------------------------------
1784
1785 if not self._PRW_age_noted.IsModified():
1786 return True
1787
1788 str_age = self._PRW_age_noted.GetValue().strip()
1789
1790 if str_age == u'':
1791 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1792 return True
1793
1794 age = gmDateTime.str2interval(str_interval = str_age)
1795
1796 if age is None:
1797 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
1798 self._PRW_age_noted.SetBackgroundColour('pink')
1799 self._PRW_age_noted.Refresh()
1800 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1801 return True
1802
1803 pat = gmPerson.gmCurrentPatient()
1804 if pat['dob'] is not None:
1805 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
1806
1807 if age >= max_age:
1808 gmDispatcher.send (
1809 signal = 'statustext',
1810 msg = _(
1811 'Health issue cannot have been noted at age %s. Patient is only %s old.'
1812 ) % (age, pat.get_medical_age())
1813 )
1814 self._PRW_age_noted.SetBackgroundColour('pink')
1815 self._PRW_age_noted.Refresh()
1816 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1817 return True
1818
1819 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1820 self._PRW_age_noted.Refresh()
1821 self._PRW_age_noted.SetData(data=age)
1822
1823 if pat['dob'] is not None:
1824 fts = gmDateTime.cFuzzyTimestamp (
1825 timestamp = pat['dob'] + age,
1826 accuracy = gmDateTime.acc_months
1827 )
1828 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
1829 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB
1830 #wx.CallAfter(self._ChBOX_active.SetFocus)
1831 # if we do the following instead it will take us to the save/update button ...
1832 #wx.CallAfter(self.Navigate)
1833
1834 return True
1835 #--------------------------------------------------------
1837
1838 if not self._PRW_year_noted.IsModified():
1839 return True
1840
1841 year_noted = self._PRW_year_noted.GetData()
1842
1843 if year_noted is None:
1844 if self._PRW_year_noted.GetValue().strip() == u'':
1845 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1846 return True
1847 self._PRW_year_noted.SetBackgroundColour('pink')
1848 self._PRW_year_noted.Refresh()
1849 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1850 return True
1851
1852 year_noted = year_noted.get_pydt()
1853
1854 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
1855 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
1856 self._PRW_year_noted.SetBackgroundColour('pink')
1857 self._PRW_year_noted.Refresh()
1858 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
1859 return True
1860
1861 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1862 self._PRW_year_noted.Refresh()
1863
1864 pat = gmPerson.gmCurrentPatient()
1865 if pat['dob'] is not None:
1866 issue_age = year_noted - pat['dob']
1867 str_age = gmDateTime.format_interval_medically(interval = issue_age)
1868 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
1869
1870 return True
1871 #--------------------------------------------------------
1875 #--------------------------------------------------------
1879 #================================================================
1880 # diagnostic certainty related widgets/functions
1881 #----------------------------------------------------------------
1883
1885
1886 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1887
1888 self.selection_only = False # can be NULL, too
1889
1890 mp = gmMatchProvider.cMatchProvider_FixedList (
1891 aSeq = [
1892 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
1893 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
1894 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
1895 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
1896 ]
1897 )
1898 mp.setThresholds(1, 2, 4)
1899 self.matcher = mp
1900
1901 self.SetToolTipString(_(
1902 "The diagnostic classification or grading of this assessment.\n"
1903 "\n"
1904 "This documents how certain one is about this being a true diagnosis."
1905 ))
1906 #================================================================
1907 # MAIN
1908 #----------------------------------------------------------------
1909 if __name__ == '__main__':
1910
1911 #================================================================
1913 """
1914 Test application for testing EMR struct widgets
1915 """
1916 #--------------------------------------------------------
1918 """
1919 Create test application UI
1920 """
1921 frame = wx.Frame (
1922 None,
1923 -4,
1924 'Testing EMR struct widgets',
1925 size=wx.Size(600, 400),
1926 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
1927 )
1928 filemenu= wx.Menu()
1929 filemenu.AppendSeparator()
1930 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
1931
1932 # Creating the menubar.
1933 menuBar = wx.MenuBar()
1934 menuBar.Append(filemenu,"&File")
1935
1936 frame.SetMenuBar(menuBar)
1937
1938 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
1939 wx.DefaultPosition, wx.DefaultSize, 0 )
1940
1941 # event handlers
1942 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
1943
1944 # patient EMR
1945 self.__pat = gmPerson.gmCurrentPatient()
1946
1947 frame.Show(1)
1948 return 1
1949 #--------------------------------------------------------
1955 #----------------------------------------------------------------
1957 app = wx.PyWidgetTester(size = (200, 300))
1958 emr = pat.get_emr()
1959 enc = emr.active_encounter
1960 #enc = gmEMRStructItems.cEncounter(1)
1961 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
1962 app.frame.Show(True)
1963 app.MainLoop()
1964 return
1965 #----------------------------------------------------------------
1967 app = wx.PyWidgetTester(size = (200, 300))
1968 emr = pat.get_emr()
1969 enc = emr.active_encounter
1970 #enc = gmEMRStructItems.cEncounter(1)
1971
1972 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
1973 dlg.ShowModal()
1974
1975 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc)
1976 # app.frame.Show(True)
1977 # app.MainLoop()
1978 #----------------------------------------------------------------
1980 app = wx.PyWidgetTester(size = (200, 300))
1981 emr = pat.get_emr()
1982 epi = emr.get_episodes()[0]
1983 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
1984 app.frame.Show(True)
1985 app.MainLoop()
1986 #----------------------------------------------------------------
1988 app = wx.PyWidgetTester(size = (200, 300))
1989 emr = pat.get_emr()
1990 epi = emr.get_episodes()[0]
1991 edit_episode(parent=app.frame, episode=epi)
1992 #----------------------------------------------------------------
1994 app = wx.PyWidgetTester(size = (400, 40))
1995 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
1996 app.MainLoop()
1997 #----------------------------------------------------------------
1999 app = wx.PyWidgetTester(size = (400, 40))
2000 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2001 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID)
2002 app.MainLoop()
2003 #----------------------------------------------------------------
2005 app = wx.PyWidgetTester(size = (200, 300))
2006 edit_health_issue(parent=app.frame, issue=None)
2007 #----------------------------------------------------------------
2009 app = wx.PyWidgetTester(size = (200, 300))
2010 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2011 app.MainLoop()
2012 #----------------------------------------------------------------
2016 #================================================================
2017
2018 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2019
2020 gmI18N.activate_locale()
2021 gmI18N.install_domain()
2022 gmDateTime.init()
2023
2024 # obtain patient
2025 pat = gmPersonSearch.ask_for_patient()
2026 if pat is None:
2027 print "No patient. Exiting gracefully..."
2028 sys.exit(0)
2029 gmPatSearchWidgets.set_active_patient(patient=pat)
2030
2031 # try:
2032 # lauch emr dialogs test application
2033 # app = testapp(0)
2034 # app.MainLoop()
2035 # except StandardError:
2036 # _log.exception("unhandled exception caught !")
2037 # but re-raise them
2038 # raise
2039
2040 #test_encounter_edit_area_panel()
2041 #test_encounter_edit_area_dialog()
2042 #test_epsiode_edit_area_pnl()
2043 #test_episode_edit_area_dialog()
2044 #test_health_issue_edit_area_dlg()
2045 #test_episode_selection_prw()
2046 #test_hospital_stay_prw()
2047 test_edit_procedure()
2048
2049 #================================================================
2050
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Oct 1 04:05:26 2010 | http://epydoc.sourceforge.net |