| 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 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 # performed procedure related widgets/functions
40 #----------------------------------------------------------------
42
43 pat = gmPerson.gmCurrentPatient()
44 emr = pat.get_emr()
45
46 if parent is None:
47 parent = wx.GetApp().GetTopWindow()
48 #-----------------------------------------
49 def edit(procedure=None):
50 return edit_procedure(parent = parent, procedure = procedure)
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 #----------------------------------------------------------------
90 ea = cProcedureEAPnl(parent = parent, id = -1)
91 ea.data = procedure
92 ea.mode = gmTools.coalesce(procedure, 'new', 'edit')
93 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
94 dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure')))
95 if dlg.ShowModal() == wx.ID_OK:
96 dlg.Destroy()
97 return True
98 dlg.Destroy()
99 return False
100 #----------------------------------------------------------------
101 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
102
104
106 wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs)
107 gmEditArea.cGenericEditAreaMixin.__init__(self)
108
109 self.mode = 'new'
110 self.data = None
111
112 self.__init_ui()
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 # location
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 # procedure
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 # self._PRW_episode.Enable(False)
161 else:
162 self._PRW_hospital_stay.SetText()
163 self._PRW_hospital_stay.Enable(False)
164 # self._PRW_episode.Enable(True)
165 #----------------------------------------------------------------
166 # generic Edit Area mixin API
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 #----------------------------------------------------------------
214
215 pat = gmPerson.gmCurrentPatient()
216 emr = pat.get_emr()
217
218 if self._PRW_hospital_stay.GetData() is None:
219 stay = None
220 epi = self._PRW_episode.GetData()
221 loc = self._PRW_location.GetValue().strip()
222 else:
223 stay = self._PRW_hospital_stay.GetData()
224 epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode']
225 loc = None
226
227 proc = emr.add_performed_procedure (
228 episode = epi,
229 location = loc,
230 hospital_stay = stay,
231 procedure = self._PRW_procedure.GetValue().strip()
232 )
233 proc['clin_when'] = self._DPRW_date.data.get_pydt()
234 proc.save()
235
236 self.data = proc
237
238 return True
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 #----------------------------------------------------------------
259 self._DPRW_date.SetText()
260 self._PRW_hospital_stay.SetText()
261 self._PRW_location.SetText()
262 self._PRW_episode.SetText()
263 self._PRW_procedure.SetText()
264
265 self._DPRW_date.SetFocus()
266 #----------------------------------------------------------------
268 self._DPRW_date.SetData(data = self.data['clin_when'])
269 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
270 self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure'])
271
272 if self.data['pk_hospital_stay'] is None:
273 self._PRW_hospital_stay.SetText()
274 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
275 else:
276 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
277 self._PRW_location.SetText()
278
279 self._DPRW_date.SetFocus()
280 #----------------------------------------------------------------
282 self._refresh_as_new()
283 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
284 if self.data['pk_hospital_stay'] is None:
285 self._PRW_hospital_stay.SetText()
286 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
287 else:
288 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
289 self._PRW_location.SetText()
290
291 self._DPRW_date.SetFocus()
292 #----------------------------------------------------------------
296 #================================================================
297 # hospital stay related widgets/functions
298 #----------------------------------------------------------------
300
301 pat = gmPerson.gmCurrentPatient()
302 emr = pat.get_emr()
303
304 if parent is None:
305 parent = wx.GetApp().GetTopWindow()
306 #-----------------------------------------
307 def edit(stay=None):
308 return edit_hospital_stay(parent = parent, hospital_stay = stay)
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 #----------------------------------------------------------------
347 ea = cHospitalStayEditAreaPnl(parent = parent, id = -1)
348 ea.data = hospital_stay
349 ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit')
350 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
351 dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospital stay'), _('Editing a hospital stay')))
352 if dlg.ShowModal() == wx.ID_OK:
353 dlg.Destroy()
354 return True
355 dlg.Destroy()
356 return False
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
414 wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl.__init__(self, *args, **kwargs)
415 gmEditArea.cGenericEditAreaMixin.__init__(self)
416 #----------------------------------------------------------------
417 # generic Edit Area mixin API
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 #----------------------------------------------------------------
442
443 pat = gmPerson.gmCurrentPatient()
444 emr = pat.get_emr()
445
446 stay = gmEMRStructItems.create_hospital_stay (
447 encounter = emr.active_encounter['pk_encounter'],
448 episode = self._PRW_episode.GetData(can_create = True)
449 )
450 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
451 stay['admission'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue())
452 if self._DP_discharge.GetValue().IsValid():
453 stay['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue())
454 stay.save_payload()
455
456 self.data = stay
457 return True
458 #----------------------------------------------------------------
460
461 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
462 self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
463 self.data['admission'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue())
464 if self._DP_discharge.GetValue().IsValid():
465 self.data['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue())
466 self.data.save_payload()
467
468 return True
469 #----------------------------------------------------------------
471 self._PRW_hospital.SetText(value = u'')
472 self._PRW_episode.SetText(value = u'')
473 self._DP_admission.SetValue(dt = wx.DateTime.UNow())
474 #self._DP_discharge.SetValue(dt = None)
475 #----------------------------------------------------------------
477 if self.data['hospital'] is not None:
478 self._PRW_hospital.SetText(value = self.data['hospital'])
479
480 if self.data['pk_episode'] is not None:
481 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
482
483 self._DP_admission.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['admission'], wx = wx))
484
485 if self.data['discharge'] is not None:
486 self._DP_discharge.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['discharge'], wx = wx))
487 #----------------------------------------------------------------
490 #================================================================
491 # encounter related widgets/functions
492 #----------------------------------------------------------------
494 emr.start_new_encounter()
495 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True)
496 time.sleep(0.5)
497 gmGuiHelpers.gm_show_info (
498 _('\nA new encounter was started for the active patient.\n'),
499 _('Start of new encounter')
500 )
501 #----------------------------------------------------------------
503
504 if parent is None:
505 parent = wx.GetApp().GetTopWindow()
506
507 # FIXME: use generic dialog 2
508 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
509 dlg.ShowModal()
510 #----------------------------------------------------------------
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 #----------------------------------------------------------------
625 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1)
626 ea.data = encounter_type
627 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit')
628 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
629 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name')))
630 if dlg.ShowModal() == wx.ID_OK:
631 return True
632 return False
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 #----------------------------------------------------------------
679 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
680
682
683 wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl.__init__(self, *args, **kwargs)
684 gmEditArea.cGenericEditAreaMixin.__init__(self)
685
686 # self.__register_interests()
687 #-------------------------------------------------------
688 # generic edit area API
689 #-------------------------------------------------------
691 if self.mode == 'edit':
692 if self._TCTRL_l10n_name.GetValue().strip() == u'':
693 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
694 return False
695 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
696 return True
697
698 no_errors = True
699
700 if self._TCTRL_l10n_name.GetValue().strip() == u'':
701 if self._TCTRL_name.GetValue().strip() == u'':
702 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
703 no_errors = False
704 else:
705 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
706 else:
707 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
708
709 if self._TCTRL_name.GetValue().strip() == u'':
710 if self._TCTRL_l10n_name.GetValue().strip() == u'':
711 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False)
712 no_errors = False
713 else:
714 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
715 else:
716 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
717
718 return no_errors
719 #-------------------------------------------------------
721 enc_type = gmEMRStructItems.create_encounter_type (
722 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''),
723 l10n_description = gmTools.coalesce (
724 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''),
725 self._TCTRL_name.GetValue().strip()
726 )
727 )
728 if enc_type is None:
729 return False
730 self.data = enc_type
731 return True
732 #-------------------------------------------------------
734 enc_type = gmEMRStructItems.update_encounter_type (
735 description = self._TCTRL_name.GetValue().strip(),
736 l10n_description = self._TCTRL_l10n_name.GetValue().strip()
737 )
738 if enc_type is None:
739 return False
740 self.data = enc_type
741 return True
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 # disallow changing type on all encounters by editing system name
752 self._TCTRL_name.Enable(False)
753 #-------------------------------------------------------
758 #-------------------------------------------------------
759 # internal API
760 #-------------------------------------------------------
761 # def __register_interests(self):
762 # return
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 # external API
784 #--------------------------------------------------------
786
787 if msg is not None:
788 self._LBL_instructions.SetLabel(msg)
789
790 if encounter is not None:
791 self.__encounter = encounter
792
793 if self.__encounter is None:
794 return True
795
796 # getting the patient via the encounter allows us to act
797 # on any encounter regardless of the currently active patient
798 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
799 self._LBL_patient.SetLabel(pat.get_description_gender())
800
801 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
802
803 fts = gmDateTime.cFuzzyTimestamp (
804 timestamp = self.__encounter['started'],
805 accuracy = gmDateTime.acc_minutes
806 )
807 self._PRW_start.SetText(fts.format_accurately(), data=fts)
808
809 fts = gmDateTime.cFuzzyTimestamp (
810 timestamp = self.__encounter['last_affirmed'],
811 accuracy = gmDateTime.acc_minutes
812 )
813 self._PRW_end.SetText(fts.format_accurately(), data=fts)
814
815 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
816 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
817
818 if self.__encounter['last_affirmed'] == self.__encounter['started']:
819 self._PRW_end.SetFocus()
820 else:
821 self._TCTRL_aoe.SetFocus()
822
823 return True
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() # FIXME: error checking
855
856 return True
857 #----------------------------------------------------------------
858 # FIXME: use generic dialog 2
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 # episode related widgets/functions
900 #----------------------------------------------------------------
902 ea = cEpisodeEditAreaPnl(parent = parent, id = -1)
903 ea.data = episode
904 ea.mode = gmTools.coalesce(episode, 'new', 'edit')
905 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
906 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode')))
907 if dlg.ShowModal() == wx.ID_OK:
908 return True
909 return False
910 #----------------------------------------------------------------
912
913 created_new_issue = False
914
915 try:
916 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient'])
917 except gmExceptions.NoSuchBusinessObjectError:
918 issue = None
919
920 if issue is None:
921 issue = emr.add_health_issue(issue_name = episode['description'])
922 created_new_issue = True
923 else:
924 # issue exists already, so ask user
925 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
926 parent,
927 -1,
928 caption = _('Promoting episode to health issue'),
929 question = _(
930 'There already is a health issue\n'
931 '\n'
932 ' %s\n'
933 '\n'
934 'What do you want to do ?'
935 ) % issue['description'],
936 button_defs = [
937 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False},
938 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True}
939 ]
940 )
941 use_existing = dlg.ShowModal()
942 dlg.Destroy()
943
944 if use_existing == wx.ID_CANCEL:
945 return
946
947 # user wants to create new issue with alternate name
948 if use_existing == wx.ID_NO:
949 # loop until name modified but non-empty or cancelled
950 issue_name = episode['description']
951 while issue_name == episode['description']:
952 dlg = wx.TextEntryDialog (
953 parent = parent,
954 message = _('Enter a short descriptive name for the new health issue:'),
955 caption = _('Creating a new health issue ...'),
956 defaultValue = issue_name,
957 style = wx.OK | wx.CANCEL | wx.CENTRE
958 )
959 decision = dlg.ShowModal()
960 if decision != wx.ID_OK:
961 dlg.Destroy()
962 return
963 issue_name = dlg.GetValue().strip()
964 dlg.Destroy()
965 if issue_name == u'':
966 issue_name = episode['description']
967
968 issue = emr.add_health_issue(issue_name = issue_name)
969 created_new_issue = True
970
971 # eventually move the episode to the issue
972 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True):
973 # user cancelled the move so delete just-created issue
974 if created_new_issue:
975 # shouldn't fail as it is completely new
976 gmEMRStructItems.delete_health_issue(health_issue = issue)
977 return
978
979 return
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 # episode is closed: should always work
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 # un-associate: should always work, too
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 # try closing possibly expired episode on target issue if any
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 # 2 months
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 # no more open episode on target issue: should work now
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 # don't conflict on SELF ;-)
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 # we got two open episodes at once, ask user
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 # button 3: move cancelled by user
1066 return False
1067
1068 elif decision == wx.ID_YES:
1069 # button 1: close old episode
1070 existing_epi['episode_open'] = False
1071 existing_epi.save_payload()
1072
1073 elif decision == wx.ID_NO:
1074 # button 2: close new episode
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 #----------------------------------------------------------------
1086
1087 # FIXME: support pre-selection
1088
1090
1091 episodes = kwargs['episodes']
1092 del kwargs['episodes']
1093
1094 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1095
1096 self.SetTitle(_('Select the episodes you are interested in ...'))
1097 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')])
1098 self._LCTRL_items.set_string_items (
1099 items = [
1100 [ epi['description'],
1101 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''),
1102 gmTools.coalesce(epi['health_issue'], u'')
1103 ]
1104 for epi in episodes ]
1105 )
1106 self._LCTRL_items.set_column_widths()
1107 self._LCTRL_items.set_data(data = episodes)
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 # external API
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 #--------------------------------------------------------
1231 self.__is_open_for_create_data = is_open # used (only) in _create_data()
1232 gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1233 return self.data
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 # internal API
1259 #--------------------------------------------------------
1261 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1262 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
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
1276
1277 try:
1278 episode = kwargs['episode']
1279 del kwargs['episode']
1280 except KeyError:
1281 episode = None
1282
1283 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
1284 gmEditArea.cGenericEditAreaMixin.__init__(self)
1285
1286 self.data = episode
1287 #----------------------------------------------------------------
1288 # generic Edit Area mixin API
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 #----------------------------------------------------------------
1359 if self.data is None:
1360 ident = gmPerson.gmCurrentPatient()
1361 else:
1362 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1363 self._TCTRL_patient.SetValue(ident.get_description_gender())
1364 self._PRW_issue.SetText()
1365 self._PRW_description.SetText()
1366 self._PRW_classification.SetText()
1367 self._CHBOX_closed.SetValue(False)
1368 #----------------------------------------------------------------
1370 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1371 self._TCTRL_patient.SetValue(ident.get_description_gender())
1372
1373 if self.data['pk_health_issue'] is not None:
1374 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue'])
1375
1376 self._PRW_description.SetText(self.data['description'], data=self.data['description'])
1377
1378 if self.data['diagnostic_certainty_classification'] is not None:
1379 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification'])
1380
1381 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1382 #----------------------------------------------------------------
1385 #================================================================
1386 # health issue related widgets/functions
1387 #----------------------------------------------------------------
1389 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1)
1390 ea.data = issue
1391 ea.mode = gmTools.coalesce(issue, 'new', 'edit')
1392 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None))
1393 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue')))
1394 if dlg.ShowModal() == wx.ID_OK:
1395 return True
1396 return False
1397 #----------------------------------------------------------------
1399
1400 # FIXME: support pre-selection
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 # FIXME: consider clin.health_issue.clinically_relevant
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 # external API
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 #--------------------------------------------------------
1504 if self.data is None:
1505 if can_create:
1506 issue_name = self.GetValue().strip()
1507
1508 if self.use_current_patient:
1509 pat = gmPerson.gmCurrentPatient()
1510 else:
1511 pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
1512 emr = pat.get_emr()
1513
1514 issue = emr.add_health_issue(issue_name = issue_name)
1515 if issue is None:
1516 self.data = None
1517 else:
1518 self.data = issue['pk_health_issue']
1519
1520 return gmPhraseWheel.cPhraseWheel.GetData(self)
1521 #--------------------------------------------------------
1522 # internal API
1523 #--------------------------------------------------------
1525 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1526 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
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 # FIXME: include more sources: coding systems/other database columns
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 # generic Edit Area mixin API
1628 #----------------------------------------------------------------
1630
1631 if self._PRW_condition.GetValue().strip() == '':
1632 self._PRW_condition.display_as_valid(False)
1633 self._PRW_condition.SetFocus()
1634 return False
1635 self._PRW_condition.display_as_valid(True)
1636 self._PRW_condition.Refresh()
1637
1638 # FIXME: sanity check age/year diagnosed
1639 age_noted = self._PRW_age_noted.GetValue().strip()
1640 if age_noted != '':
1641 if gmDateTime.str2interval(str_interval = age_noted) is None:
1642 self._PRW_age_noted.display_as_valid(False)
1643 self._PRW_age_noted.SetFocus()
1644 return False
1645 self._PRW_age_noted.display_as_valid(True)
1646
1647 return True
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 # update self.data and save the changes
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 # FIXME: handle is_operation
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) # FIXME
1764 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
1765 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
1766
1767 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ...
1768 # self._PRW_age_noted.SetFocus()
1769 # self._PRW_condition.SetFocus()
1770
1771 return True
1772 #----------------------------------------------------------------
1775 #--------------------------------------------------------
1776 # internal helpers
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 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB
1825 #wx.CallAfter(self._ChBOX_active.SetFocus)
1826 # if we do the following instead it will take us to the save/update button ...
1827 #wx.CallAfter(self.Navigate)
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 #--------------------------------------------------------
1870 #--------------------------------------------------------
1874 #================================================================
1875 # diagnostic certainty related widgets/functions
1876 #----------------------------------------------------------------
1878
1880
1881 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1882
1883 self.selection_only = False # can be NULL, too
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 # MAIN
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 # Creating the menubar.
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 # event handlers
1937 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
1938
1939 # patient EMR
1940 self.__pat = gmPerson.gmCurrentPatient()
1941
1942 frame.Show(1)
1943 return 1
1944 #--------------------------------------------------------
1950 #----------------------------------------------------------------
1952 app = wx.PyWidgetTester(size = (200, 300))
1953 emr = pat.get_emr()
1954 enc = emr.active_encounter
1955 #enc = gmEMRStructItems.cEncounter(1)
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 #enc = gmEMRStructItems.cEncounter(1)
1966
1967 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
1968 dlg.ShowModal()
1969
1970 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc)
1971 # app.frame.Show(True)
1972 # app.MainLoop()
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 #----------------------------------------------------------------
1983 app = wx.PyWidgetTester(size = (200, 300))
1984 emr = pat.get_emr()
1985 epi = emr.get_episodes()[0]
1986 edit_episode(parent=app.frame, episode=epi)
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 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID)
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 #----------------------------------------------------------------
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 # obtain patient
2020 pat = gmPersonSearch.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 # try:
2027 # lauch emr dialogs test application
2028 # app = testapp(0)
2029 # app.MainLoop()
2030 # except StandardError:
2031 # _log.exception("unhandled exception caught !")
2032 # but re-raise them
2033 # raise
2034
2035 #test_encounter_edit_area_panel()
2036 #test_encounter_edit_area_dialog()
2037 #test_epsiode_edit_area_pnl()
2038 #test_episode_edit_area_dialog()
2039 #test_health_issue_edit_area_dlg()
2040 #test_episode_selection_prw()
2041 #test_hospital_stay_prw()
2042 test_edit_procedure()
2043
2044 #================================================================
2045
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Sep 9 04:07:51 2010 | http://epydoc.sourceforge.net |