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