| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed patient EMR tree browser.
2 """
3 #================================================================
4 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gmEMRBrowser.py,v $
5 # $Id: gmEMRBrowser.py,v 1.111 2010-01-11 19:44:39 ncq Exp $
6 __version__ = "$Revision: 1.111 $"
7 __author__ = "cfmoro1976@yahoo.es, sjtan@swiftdsl.com.au, Karsten.Hilbert@gmx.net"
8 __license__ = "GPL"
9
10 # std lib
11 import sys, types, os.path, StringIO, codecs, logging
12
13 # 3rd party
14 import wx
15
16 # GNUmed libs
17 from Gnumed.pycommon import gmI18N, gmDispatcher, gmExceptions, gmTools
18 from Gnumed.exporters import gmPatientExporter
19 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter
20 from Gnumed.wxpython import gmGuiHelpers, gmEMRStructWidgets, gmSOAPWidgets, gmAllergyWidgets, gmNarrativeWidgets, gmPatSearchWidgets
21 from Gnumed.wxGladeWidgets import wxgScrolledEMRTreePnl, wxgSplittedEMRTreeBrowserPnl
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26 #============================================================
28 """
29 Dump the patient's EMR from GUI client
30 @param parent - The parent widget
31 @type parent - A wx.Window instance
32 """
33 # sanity checks
34 if parent is None:
35 raise TypeError('expected wx.Window instance as parent, got <None>')
36
37 pat = gmPerson.gmCurrentPatient()
38 if not pat.connected:
39 gmDispatcher.send(signal='statustext', msg=_('Cannot export EMR. No active patient.'))
40 return False
41
42 # get file name
43 wc = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
44 defdir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])))
45 gmTools.mkdir(defdir)
46 fname = '%s-%s_%s.txt' % (_('emr-export'), pat['lastnames'], pat['firstnames'])
47 dlg = wx.FileDialog (
48 parent = parent,
49 message = _("Save patient's EMR as..."),
50 defaultDir = defdir,
51 defaultFile = fname,
52 wildcard = wc,
53 style = wx.SAVE
54 )
55 choice = dlg.ShowModal()
56 fname = dlg.GetPath()
57 dlg.Destroy()
58 if choice != wx.ID_OK:
59 return None
60
61 _log.debug('exporting EMR to [%s]', fname)
62
63 # output_file = open(fname, 'wb')
64 output_file = codecs.open(fname, 'wb', encoding='utf8', errors='replace')
65 exporter = gmPatientExporter.cEmrExport(patient = pat)
66 exporter.set_output_file(output_file)
67 exporter.dump_constraints()
68 exporter.dump_demographic_record(True)
69 exporter.dump_clinical_record()
70 exporter.dump_med_docs()
71 output_file.close()
72
73 gmDispatcher.send('statustext', msg = _('EMR successfully exported to file: %s') % fname, beep = False)
74 return fname
75 #============================================================
77 """This wx.TreeCtrl derivative displays a tree view of the medical record."""
78
79 #--------------------------------------------------------
81 """Set up our specialised tree.
82 """
83 kwds['style'] = wx.TR_HAS_BUTTONS | wx.NO_BORDER
84 wx.TreeCtrl.__init__(self, parent, id, *args, **kwds)
85
86 gmGuiHelpers.cTreeExpansionHistoryMixin.__init__(self)
87
88 try:
89 self.__narr_display = kwds['narr_display']
90 del kwds['narr_display']
91 except KeyError:
92 self.__narr_display = None
93 self.__pat = gmPerson.gmCurrentPatient()
94 self.__curr_node = None
95 self.__exporter = gmPatientExporter.cEmrExport(patient = self.__pat)
96
97 self._old_cursor_pos = None
98
99 self.__make_popup_menus()
100 self.__register_events()
101 #--------------------------------------------------------
102 # external API
103 #--------------------------------------------------------
105 if not self.__pat.connected:
106 gmDispatcher.send(signal='statustext', msg=_('Cannot load clinical narrative. No active patient.'),)
107 return False
108
109 if not self.__populate_tree():
110 return False
111
112 return True
113 #--------------------------------------------------------
116 #--------------------------------------------------------
117 # internal helpers
118 #--------------------------------------------------------
120 """Configures enabled event signals."""
121 wx.EVT_TREE_SEL_CHANGED (self, self.GetId(), self._on_tree_item_selected)
122 wx.EVT_TREE_ITEM_RIGHT_CLICK (self, self.GetId(), self._on_tree_item_right_clicked)
123
124 # handle tooltips
125 # wx.EVT_MOTION(self, self._on_mouse_motion)
126 wx.EVT_TREE_ITEM_GETTOOLTIP(self, -1, self._on_tree_item_gettooltip)
127
128 gmDispatcher.connect(signal = 'narrative_mod_db', receiver = self._on_narrative_mod_db)
129 gmDispatcher.connect(signal = 'episode_mod_db', receiver = self._on_episode_mod_db)
130 gmDispatcher.connect(signal = 'health_issue_mod_db', receiver = self._on_issue_mod_db)
131 #--------------------------------------------------------
133 """Updates EMR browser data."""
134 # FIXME: auto select the previously self.__curr_node if not None
135 # FIXME: error handling
136
137 wx.BeginBusyCursor()
138
139 self.snapshot_expansion()
140
141 # init new tree
142 self.DeleteAllItems()
143 root_item = self.AddRoot(_('EMR of %s') % self.__pat['description'])
144 self.SetPyData(root_item, None)
145 self.SetItemHasChildren(root_item, True)
146
147 # have the tree filled by the exporter
148 self.__exporter.get_historical_tree(self)
149 self.__curr_node = root_item
150
151 self.SelectItem(root_item)
152 self.Expand(root_item)
153 self.__update_text_for_selected_node()
154
155 self.restore_expansion()
156
157 wx.EndBusyCursor()
158 return True
159 #--------------------------------------------------------
161 """Displays information for the selected tree node."""
162
163 if self.__narr_display is None:
164 return
165
166 if self.__curr_node is None:
167 return
168
169 node_data = self.GetPyData(self.__curr_node)
170
171 # update displayed text
172 if isinstance(node_data, (gmEMRStructItems.cHealthIssue, types.DictType)):
173 # FIXME: turn into real dummy issue
174 if node_data['pk_health_issue'] is None:
175 txt = _('Pool of unassociated episodes:\n\n "%s"') % node_data['description']
176 else:
177 txt = node_data.format(left_margin=1, patient = self.__pat)
178
179 elif isinstance(node_data, gmEMRStructItems.cEpisode):
180 txt = node_data.format(left_margin = 1, patient = self.__pat)
181
182 elif isinstance(node_data, gmEMRStructItems.cEncounter):
183 epi = self.GetPyData(self.GetItemParent(self.__curr_node))
184 txt = node_data.format(episodes = [epi['pk_episode']], with_soap = True, left_margin = 1, patient = self.__pat)
185
186 else:
187 emr = self.__pat.get_emr()
188 txt = emr.format_summary()
189
190 self.__narr_display.Clear()
191 self.__narr_display.WriteText(txt)
192 #--------------------------------------------------------
281 #--------------------------------------------------------
284 #--------------------------------------------------------
286 # self.__issue_context_popup.SetTitle(_('Episode %s') % episode['description'])
287 self.PopupMenu(self.__issue_context_popup, pos)
288 #--------------------------------------------------------
290 self.__epi_context_popup.SetTitle(_('Episode %s') % self.__curr_node_data['description'])
291 self.PopupMenu(self.__epi_context_popup, pos)
292 #--------------------------------------------------------
295 #--------------------------------------------------------
296 # episode level
297 #--------------------------------------------------------
299 episode = self.GetPyData(self.__curr_node)
300
301 gmNarrativeWidgets.move_progress_notes_to_another_encounter (
302 parent = self,
303 episodes = [episode['pk_episode']],
304 move_all = True
305 )
306 #--------------------------------------------------------
309 #--------------------------------------------------------
311 pat = gmPerson.gmCurrentPatient()
312 gmEMRStructWidgets.promote_episode_to_issue(parent=self, episode = self.__curr_node_data, emr = pat.get_emr())
313 #--------------------------------------------------------
315 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
316 parent = self,
317 id = -1,
318 caption = _('Deleting episode'),
319 button_defs = [
320 {'label': _('Yes, delete'), 'tooltip': _('Delete the episode if possible (it must be completely empty).')},
321 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the episode.')}
322 ],
323 question = _(
324 'Are you sure you want to delete this episode ?\n'
325 '\n'
326 ' "%s"\n'
327 ) % self.__curr_node_data['description']
328 )
329 result = dlg.ShowModal()
330 if result != wx.ID_YES:
331 return
332
333 try:
334 gmEMRStructItems.delete_episode(episode = self.__curr_node_data)
335 except gmExceptions.DatabaseObjectInUseError:
336 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete episode. There is still clinical data recorded for it.'))
337 return
338 #--------------------------------------------------------
339 # encounter level
340 #--------------------------------------------------------
342 encounter = self.GetPyData(self.__curr_node)
343 node_parent = self.GetItemParent(self.__curr_node)
344 episode = self.GetPyData(node_parent)
345
346 gmNarrativeWidgets.move_progress_notes_to_another_encounter (
347 parent = self,
348 encounters = [encounter['pk_encounter']],
349 episodes = [episode['pk_episode']]
350 )
351 #--------------------------------------------------------
353 encounter = self.GetPyData(self.__curr_node)
354 node_parent = self.GetItemParent(self.__curr_node)
355 episode = self.GetPyData(node_parent)
356
357 gmNarrativeWidgets.manage_progress_notes (
358 parent = self,
359 encounters = [encounter['pk_encounter']],
360 episodes = [episode['pk_episode']]
361 )
362 #--------------------------------------------------------
364 node_data = self.GetPyData(self.__curr_node)
365 dlg = gmEMRStructWidgets.cEncounterEditAreaDlg(parent=self, encounter=node_data)
366 dlg.ShowModal()
367 dlg.Destroy()
368 self.__populate_tree()
369 #--------------------------------------------------------
371
372 node_parent = self.GetItemParent(self.__curr_node)
373 owning_episode = self.GetPyData(node_parent)
374
375 episode_selector = gmNarrativeWidgets.cMoveNarrativeDlg (
376 self,
377 -1,
378 episode = owning_episode,
379 encounter = self.__curr_node_data
380 )
381
382 result = episode_selector.ShowModal()
383 episode_selector.Destroy()
384
385 if result == wx.ID_YES:
386 self.__populate_tree()
387 #--------------------------------------------------------
390 #--------------------------------------------------------
392 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
393 parent = self,
394 id = -1,
395 caption = _('Deleting health issue'),
396 button_defs = [
397 {'label': _('Yes, delete'), 'tooltip': _('Delete the health issue if possible (it must be completely empty).')},
398 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the health issue.')}
399 ],
400 question = _(
401 'Are you sure you want to delete this health issue ?\n'
402 '\n'
403 ' "%s"\n'
404 ) % self.__curr_node_data['description']
405 )
406 result = dlg.ShowModal()
407 if result != wx.ID_YES:
408 dlg.Destroy()
409 return
410
411 dlg.Destroy()
412
413 try:
414 gmEMRStructItems.delete_health_issue(health_issue = self.__curr_node_data)
415 except gmExceptions.DatabaseObjectInUseError:
416 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete health issue. There is still clinical data recorded for it.'))
417 #--------------------------------------------------------
419
420 if not self.__curr_node.IsOk():
421 return
422
423 self.Expand(self.__curr_node)
424
425 epi, epi_cookie = self.GetFirstChild(self.__curr_node)
426 while epi.IsOk():
427 self.Expand(epi)
428 epi, epi_cookie = self.GetNextChild(self.__curr_node, epi_cookie)
429 #--------------------------------------------------------
432 #--------------------------------------------------------
434 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1)
435 # FIXME: use signal and use node level update
436 if dlg.ShowModal() == wx.ID_OK:
437 self.__populate_tree()
438 dlg.Destroy()
439 return
440 #--------------------------------------------------------
442
443 root_item = self.GetRootItem()
444
445 if not root_item.IsOk():
446 return
447
448 self.Expand(root_item)
449
450 # collapse episodes and issues
451 issue, issue_cookie = self.GetFirstChild(root_item)
452 while issue.IsOk():
453 self.Collapse(issue)
454 epi, epi_cookie = self.GetFirstChild(issue)
455 while epi.IsOk():
456 self.Collapse(epi)
457 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
458 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
459 #--------------------------------------------------------
461
462 root_item = self.GetRootItem()
463
464 if not root_item.IsOk():
465 return
466
467 self.Expand(root_item)
468
469 # collapse episodes, expand issues
470 issue, issue_cookie = self.GetFirstChild(root_item)
471 while issue.IsOk():
472 self.Expand(issue)
473 epi, epi_cookie = self.GetFirstChild(issue)
474 while epi.IsOk():
475 self.Collapse(epi)
476 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
477 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
478 #--------------------------------------------------------
480
481 root_item = self.GetRootItem()
482
483 if not root_item.IsOk():
484 return
485
486 self.Expand(root_item)
487
488 # collapse episodes, expand issues
489 issue, issue_cookie = self.GetFirstChild(root_item)
490 while issue.IsOk():
491 self.Expand(issue)
492 epi, epi_cookie = self.GetFirstChild(issue)
493 while epi.IsOk():
494 self.Expand(epi)
495 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
496 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
497 #--------------------------------------------------------
499 gmNarrativeWidgets.export_narrative_for_medistar_import (
500 parent = self,
501 soap_cats = u'soap',
502 encounter = self.__curr_node_data
503 )
504 #--------------------------------------------------------
505 # event handlers
506 #--------------------------------------------------------
509 #--------------------------------------------------------
512 #--------------------------------------------------------
515 #--------------------------------------------------------
517 sel_item = event.GetItem()
518 self.__curr_node = sel_item
519 self.__update_text_for_selected_node()
520 return True
521 # #--------------------------------------------------------
522 # def _on_mouse_motion(self, event):
523 #
524 # cursor_pos = (event.GetX(), event.GetY())
525 #
526 # self.SetToolTipString(u'')
527 #
528 # if cursor_pos != self._old_cursor_pos:
529 # self._old_cursor_pos = cursor_pos
530 # (item, flags) = self.HitTest(cursor_pos)
531 # #if flags != wx.TREE_HITTEST_NOWHERE:
532 # if flags == wx.TREE_HITTEST_ONITEMLABEL:
533 # data = self.GetPyData(item)
534 #
535 # if not isinstance(data, gmEMRStructItems.cEncounter):
536 # return
537 #
538 # self.SetToolTip(u'%s %s %s - %s\n\nRFE: %s\nAOE: %s' % (
539 # data['started'].strftime('%x'),
540 # data['l10n_type'],
541 # data['started'].strftime('%H:%m'),
542 # data['last_affirmed'].strftime('%H:%m'),
543 # gmTools.coalesce(data['reason_for_encounter'], u''),
544 # gmTools.coalesce(data['assessment_of_encounter'], u'')
545 # ))
546 #--------------------------------------------------------
548
549 item = event.GetItem()
550
551 if not item.IsOk():
552 event.SetToolTip(u' ')
553 return
554
555 data = self.GetPyData(item)
556
557 if isinstance(data, gmEMRStructItems.cEncounter):
558 event.SetToolTip(u'%s %s %s - %s\n\nRFE: %s\nAOE: %s' % (
559 data['started'].strftime('%x'),
560 data['l10n_type'],
561 data['started'].strftime('%H:%M'),
562 data['last_affirmed'].strftime('%H:%M'),
563 gmTools.coalesce(data['reason_for_encounter'], u''),
564 gmTools.coalesce(data['assessment_of_encounter'], u'')
565 ))
566
567 elif isinstance(data, gmEMRStructItems.cEpisode):
568 tt = u''
569 tt += gmTools.bool2subst (
570 (data['diagnostic_certainty_classification'] is not None),
571 data.diagnostic_certainty_description + u'\n\n',
572 u''
573 )
574 tt += gmTools.bool2subst (
575 data['episode_open'],
576 _('ongoing episode'),
577 _('closed episode'),
578 'error: episode state is None'
579 )
580 if tt == u'':
581 tt = u' '
582 event.SetToolTip(tt)
583
584 elif isinstance(data, gmEMRStructItems.cHealthIssue):
585 tt = u''
586 tt += gmTools.bool2subst(data['is_confidential'], _('*** CONFIDENTIAL ***\n\n'), u'')
587 tt += gmTools.bool2subst (
588 (data['diagnostic_certainty_classification'] is not None),
589 data.diagnostic_certainty_description + u'\n',
590 u''
591 )
592 tt += gmTools.bool2subst (
593 (data['laterality'] not in [None, u'na']),
594 data.laterality_description + u'\n',
595 u''
596 )
597 # noted_at_age is too costly
598 tt += gmTools.bool2subst(data['is_active'], _('active') + u'\n', u'')
599 tt += gmTools.bool2subst(data['clinically_relevant'], _('clinically relevant') + u'\n', u'')
600 tt += gmTools.bool2subst(data['is_cause_of_death'], _('contributed to death') + u'\n', u'')
601 tt += gmTools.coalesce(data['grouping'], u'', _('Grouping: %s'))
602 if tt == u'':
603 tt = u' '
604 event.SetToolTip(tt)
605
606 else:
607 event.SetToolTip(u' ')
608 #self.SetToolTipString(u'')
609
610 # doing this prevents the tooltip from showing at all
611 #event.Skip()
612
613 #widgetXY.GetToolTip().Enable(False)
614 #
615 #seems to work, supposing the tooltip is actually set for the widget,
616 #otherwise a test would be needed
617 #if widgetXY.GetToolTip():
618 # widgetXY.GetToolTip().Enable(False)
619 #--------------------------------------------------------
621 """Right button clicked: display the popup for the tree"""
622
623 node = event.GetItem()
624 self.SelectItem(node)
625 self.__curr_node_data = self.GetPyData(node)
626 self.__curr_node = node
627
628 pos = wx.DefaultPosition
629 if isinstance(self.__curr_node_data, gmEMRStructItems.cHealthIssue):
630 self.__handle_issue_context(pos=pos)
631 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEpisode):
632 self.__handle_episode_context(pos=pos)
633 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEncounter):
634 self.__handle_encounter_context(pos=pos)
635 elif node == self.GetRootItem():
636 self.__handle_root_context()
637 elif type(self.__curr_node_data) == type({}):
638 # ignore pseudo node "free-standing episodes"
639 pass
640 else:
641 print "error: unknown node type, no popup menu"
642 event.Skip()
643 #--------------------------------------------------------
645 """Used in sorting items.
646
647 -1: 1 < 2
648 0: 1 = 2
649 1: 1 > 2
650 """
651 # FIXME: implement sort modes, chron, reverse cron, by regex, etc
652
653 item1 = self.GetPyData(node1)
654 item2 = self.GetPyData(node2)
655
656 # encounters: reverse chron
657 if isinstance(item1, gmEMRStructItems.cEncounter):
658 if item1['started'] == item2['started']:
659 return 0
660 if item1['started'] > item2['started']:
661 return -1
662 return 1
663
664 # episodes: chron
665 if isinstance(item1, gmEMRStructItems.cEpisode):
666 start1 = item1.get_access_range()[0]
667 start2 = item2.get_access_range()[0]
668 if start1 == start2:
669 return 0
670 if start1 < start2:
671 return -1
672 return 1
673
674 # issues: alpha by grouping, no grouping at the bottom
675 if isinstance(item1, gmEMRStructItems.cHealthIssue):
676
677 # no grouping below grouping
678 if item1['grouping'] is None:
679 if item2['grouping'] is not None:
680 return 1
681
682 # grouping above no grouping
683 if item1['grouping'] is not None:
684 if item2['grouping'] is None:
685 return -1
686
687 # both no grouping: alpha on description
688 if (item1['grouping'] is None) and (item2['grouping'] is None):
689 if item1['description'].lower() < item2['description'].lower():
690 return -1
691 if item1['description'].lower() > item2['description'].lower():
692 return 1
693 return 0
694
695 # both with grouping: alpha on grouping, then alpha on description
696 if item1['grouping'] < item2['grouping']:
697 return -1
698
699 if item1['grouping'] > item2['grouping']:
700 return 1
701
702 if item1['description'].lower() < item2['description'].lower():
703 return -1
704
705 if item1['description'].lower() > item2['description'].lower():
706 return 1
707
708 return 0
709
710 # dummy health issue always on top
711 if isinstance(item1, type({})):
712 return -1
713
714 return 0
715 #================================================================
717 """A scrollable panel holding an EMR tree.
718
719 Lacks a widget to display details for selected items. The
720 tree data will be refetched - if necessary - whenever
721 repopulate_ui() is called, e.g., when then patient is changed.
722 """
725 # self.__register_interests()
726 #--------------------------------------------------------
727 # def __register_interests(self):
728 # gmDispatcher.connect(signal= u'post_patient_selection', receiver=self.repopulate_ui)
729 #--------------------------------------------------------
733 #============================================================
735 """A splitter window holding an EMR tree.
736
737 The left hand side displays a scrollable EMR tree while
738 on the right details for selected items are displayed.
739
740 Expects to be put into a Notebook.
741 """
743 wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl.__init__(self, *args, **kwds)
744 self._pnl_emr_tree._emr_tree.set_narrative_display(narrative_display = self._TCTRL_item_details)
745 self.__register_events()
746 #--------------------------------------------------------
748 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
749 return True
750 #--------------------------------------------------------
755 #--------------------------------------------------------
757 """Fills UI with data."""
758 self._pnl_emr_tree.repopulate_ui()
759 self._splitter_browser.SetSashPosition(self._splitter_browser.GetSizeTuple()[0]/3, True)
760 return True
761 #================================================================
764 wx.Panel.__init__(self, *args, **kwargs)
765
766 self.__do_layout()
767 self.__register_events()
768 #--------------------------------------------------------
770 self.__journal = wx.TextCtrl (
771 self,
772 -1,
773 _('No EMR data loaded.'),
774 style = wx.TE_MULTILINE | wx.TE_READONLY
775 )
776 self.__journal.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL))
777 # arrange widgets
778 szr_outer = wx.BoxSizer(wx.VERTICAL)
779 szr_outer.Add(self.__journal, 1, wx.EXPAND, 0)
780 # and do layout
781 self.SetAutoLayout(1)
782 self.SetSizer(szr_outer)
783 szr_outer.Fit(self)
784 szr_outer.SetSizeHints(self)
785 self.Layout()
786 #--------------------------------------------------------
788 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
789 #--------------------------------------------------------
791 """Expects to be in a Notebook."""
792 if self.GetParent().GetCurrentPage() == self:
793 self.repopulate_ui()
794 return True
795 #--------------------------------------------------------
796 # notebook plugin API
797 #--------------------------------------------------------
799 txt = StringIO.StringIO()
800 exporter = gmPatientExporter.cEMRJournalExporter()
801 # FIXME: if journal is large this will error out, use generator/yield etc
802 # FIXME: turn into proper list
803 try:
804 exporter.export(txt)
805 self.__journal.SetValue(txt.getvalue())
806 except ValueError:
807 _log.exception('cannot get EMR journal')
808 self.__journal.SetValue (_(
809 'An error occurred while retrieving the EMR\n'
810 'in journal form for the active patient.\n\n'
811 'Please check the log file for details.'
812 ))
813 txt.close()
814 self.__journal.ShowPosition(self.__journal.GetLastPosition())
815 return True
816 #================================================================
817 # MAIN
818 #----------------------------------------------------------------
819 if __name__ == '__main__':
820
821 _log.info("starting emr browser...")
822
823 try:
824 # obtain patient
825 patient = gmPerson.ask_for_patient()
826 if patient is None:
827 print "No patient. Exiting gracefully..."
828 sys.exit(0)
829 gmPatSearchWidgets.set_active_patient(patient = patient)
830
831 # display standalone browser
832 application = wx.PyWidgetTester(size=(800,600))
833 emr_browser = cEMRBrowserPanel(application.frame, -1)
834 emr_browser.refresh_tree()
835
836 application.frame.Show(True)
837 application.MainLoop()
838
839 # clean up
840 if patient is not None:
841 try:
842 patient.cleanup()
843 except:
844 print "error cleaning up patient"
845 except StandardError:
846 _log.exception("unhandled exception caught !")
847 # but re-raise them
848 raise
849
850 _log.info("closing emr browser...")
851
852 #================================================================
853 # $Log: gmEMRBrowser.py,v $
854 # Revision 1.111 2010-01-11 19:44:39 ncq
855 # - cleanup
856 #
857 # Revision 1.110 2009/11/15 01:05:02 ncq
858 # - enable moving SOAP of a list of encounters of an episode
859 #
860 # Revision 1.109 2009/11/06 15:17:07 ncq
861 # - properly promote episode to issue
862 # - better tooltips
863 #
864 # Revision 1.108 2009/10/29 17:21:14 ncq
865 # - improved tooltips
866 #
867 # Revision 1.107 2009/10/20 10:26:21 ncq
868 # - tooltips on issue/episode in EMR tree
869 #
870 # Revision 1.106 2009/09/23 14:34:21 ncq
871 # - add promoting episode to issue
872 #
873 # Revision 1.105 2009/09/01 22:26:56 ncq
874 # - use new edit_episode/edit_health_issue
875 #
876 # Revision 1.104 2009/06/04 16:30:30 ncq
877 # - use set active patient from pat search widgets
878 #
879 # Revision 1.103 2009/05/13 12:19:13 ncq
880 # - use improved encounter management
881 # - add moving narrative between encounters
882 #
883 # Revision 1.102 2009/04/16 12:48:31 ncq
884 # - edit_progress_notes -> manage_*
885 # - use proper formatter when displaying encounter tooltip
886 #
887 # Revision 1.101 2009/04/05 18:04:46 ncq
888 # - support and use grouping
889 #
890 # Revision 1.100 2009/01/21 18:03:53 ncq
891 # - comment on tooltip handling
892 #
893 # Revision 1.99 2008/12/18 21:27:56 ncq
894 # - add editing progress notes from encounter context menu
895 #
896 # Revision 1.98 2008/12/01 12:37:37 ncq
897 # - generate encounter node tooltips
898 #
899 # Revision 1.97 2008/11/20 19:50:19 ncq
900 # - use improved data formatting
901 #
902 # Revision 1.96 2008/10/12 16:13:23 ncq
903 # - rename EMR tree root node, per Jim
904 #
905 # Revision 1.95 2008/09/02 19:01:11 ncq
906 # - adjust to clin health_issue fk_patient drop and related changes
907 #
908 # Revision 1.94 2008/08/31 18:39:47 ncq
909 # - if narrative is added, say a test result with comment, before
910 # the tree was ever displayed (hence not populated) for that patient
911 # there is not yet a currently selected node, so don't update
912 # the narrative display either
913 #
914 # Revision 1.93 2008/08/15 15:56:38 ncq
915 # - properly handle context click on pseudo-issue
916 #
917 # Revision 1.92 2008/07/28 15:44:39 ncq
918 # - context menu based Medistar export for any encounter
919 #
920 # Revision 1.91 2008/07/14 13:46:11 ncq
921 # - better naming of dummy health issue
922 #
923 # Revision 1.90 2008/07/12 15:31:23 ncq
924 # - improved formatting of issue info
925 #
926 # Revision 1.89 2008/07/07 13:44:33 ncq
927 # - current patient .connected
928 # - properly sort tree, encounters: most recent on top as per user request
929 #
930 # Revision 1.88 2008/06/28 18:25:58 ncq
931 # - add expand to ... level popup menu items in EMR tree
932 #
933 # Revision 1.87 2008/05/19 16:23:33 ncq
934 # - let EMR format its summary itself
935 #
936 # Revision 1.86 2008/04/11 12:27:45 ncq
937 # - listen to issue/episode/narrative change signals thereby
938 # reducing direct repopulate calls
939 # - factor out __update_text_for_selected_node() and
940 # call format() on nodes that have it
941 # - rearrange code layout
942 #
943 # Revision 1.85 2008/03/05 22:30:14 ncq
944 # - new style logging
945 #
946 # Revision 1.84 2008/01/30 14:07:24 ncq
947 # - do not use old cfg file support anymore
948 #
949 # Revision 1.83 2008/01/22 12:20:53 ncq
950 # - dummy health issue always on top
951 # - auto-scroll to bottom of journal
952 #
953 # Revision 1.82 2007/12/11 12:49:25 ncq
954 # - explicit signal handling
955 #
956 # Revision 1.81 2007/09/07 10:56:57 ncq
957 # - cleanup
958 #
959 # Revision 1.80 2007/08/29 22:09:10 ncq
960 # - narrative widgets factored out
961 #
962 # Revision 1.79 2007/08/15 14:57:52 ncq
963 # - pretty up tree popup menus
964 # - add deletion of health issues
965 #
966 # Revision 1.78 2007/08/12 00:09:07 ncq
967 # - no more gmSignals.py
968 #
969 # Revision 1.77 2007/06/18 20:31:10 ncq
970 # - case insensitively sort health issues
971 #
972 # Revision 1.76 2007/06/10 09:56:54 ncq
973 # - actually sort tree items, add sorting for health issues
974 #
975 # Revision 1.75 2007/05/21 14:48:20 ncq
976 # - cleanup
977 # - use pat['dirname'], use export/EMR/
978 # - unicode output files
979 #
980 # Revision 1.74 2007/05/21 13:05:25 ncq
981 # - catch-all wildcard on UNIX must be *, not *.*
982 #
983 # Revision 1.73 2007/05/18 13:29:25 ncq
984 # - some cleanup
985 # - properly support moving narrative between episodes
986 #
987 # Revision 1.72 2007/05/14 13:11:24 ncq
988 # - use statustext() signal
989 #
990 # Revision 1.71 2007/05/14 10:33:33 ncq
991 # - allow deleting episode
992 #
993 # Revision 1.70 2007/03/18 14:04:00 ncq
994 # - add allergy handling to menu and root node of tree
995 #
996 # Revision 1.69 2007/03/02 15:31:45 ncq
997 # - properly repopulation EMR tree and problem list :-)
998 #
999 # Revision 1.68 2007/02/22 17:41:13 ncq
1000 # - adjust to gmPerson changes
1001 #
1002 # Revision 1.67 2007/02/16 12:51:46 ncq
1003 # - fix add issue popup on root node as requested by user :-)
1004 #
1005 # Revision 1.66 2007/01/16 18:00:59 ncq
1006 # - cleanup
1007 # - explicitely sort episodes and encounters by when they were started
1008 #
1009 # Revision 1.65 2007/01/15 13:08:55 ncq
1010 # - remove explicit __relink_episode2issue as episode details editor now does it
1011 #
1012 # Revision 1.64 2007/01/13 22:26:55 ncq
1013 # - remove cruft
1014 # - mix expansion history into emr tree browser
1015 #
1016 # Revision 1.63 2007/01/06 23:41:40 ncq
1017 # - missing :
1018 #
1019 # Revision 1.62 2007/01/04 23:41:36 ncq
1020 # - use new episode edit area
1021 #
1022 # Revision 1.61 2006/12/25 22:50:50 ncq
1023 # - add editing of consultation details from EMR tree right-click popup menu
1024 #
1025 # Revision 1.60 2006/12/13 23:32:41 ncq
1026 # - emr journal on a diet
1027 #
1028 # Revision 1.59 2006/11/24 14:20:44 ncq
1029 # - used shiny new health issue edit area in issue context menu
1030 # - refresh tree after editing health issue
1031 #
1032 # Revision 1.58 2006/11/24 10:01:31 ncq
1033 # - gm_beep_statustext() -> gm_statustext()
1034 #
1035 # Revision 1.57 2006/11/05 16:02:00 ncq
1036 # - cleanup
1037 #
1038 # Revision 1.56 2006/10/09 12:22:27 ncq
1039 # - some cleanup
1040 # - adjust to changed signature of encounter.transfer_clinical_data()
1041 #
1042 # Revision 1.55 2006/06/26 13:03:22 ncq
1043 # - improve menu strings
1044 # - implement moving episodes among issues
1045 #
1046 # Revision 1.54 2006/05/28 20:53:28 ncq
1047 # - cleanup, fix some variables and typos
1048 #
1049 # Revision 1.53 2006/05/28 16:23:10 ncq
1050 # - cleanup
1051 # - dedicated cEMRTree akin to cDocTree
1052 # - wxGladify tree widgets
1053 #
1054 # Revision 1.52 2006/05/15 13:35:59 ncq
1055 # - signal cleanup:
1056 # - activating_patient -> pre_patient_selection
1057 # - patient_selected -> post_patient_selection
1058 #
1059 # Revision 1.51 2006/05/04 09:49:20 ncq
1060 # - get_clinical_record() -> get_emr()
1061 # - adjust to changes in set_active_patient()
1062 # - need explicit set_active_patient() after ask_for_patient() if wanted
1063 #
1064 # Revision 1.50 2005/12/27 02:52:40 sjtan
1065 #
1066 # allow choice of closing old episode, or relinking to old episode, whenever opening a new episode in the present of an already open episode of an issue.
1067 # Small logic error fixed where the id of the health_issue was passed in as the id of an episode.
1068 #
1069 # Revision 1.49 2005/10/18 13:34:00 sjtan
1070 # after running; small diffs
1071 #
1072 # Revision 1.48 2005/10/09 06:42:02 sjtan
1073 # timely cache update means a complete tree reconstruct can be done quite fast ( currently sized records anyway),
1074 # so don't use refresh_historical_tree() - need to debug this anyway.
1075 #
1076 # Revision 1.47 2005/10/08 12:33:10 sjtan
1077 # tree can be updated now without refetching entire cache; done by passing emr object to create_xxxx methods and calling emr.update_cache(key,obj);refresh_historical_tree non-destructively checks for changes and removes removed nodes and adds them if cache mismatch.
1078 #
1079 # Revision 1.46 2005/10/04 19:24:53 sjtan
1080 # browser now remembers expansion state and select state between change of patients, between health issue rename, episode rename or encounter relinking. This helps when reviewing the record more than once in a day.
1081 #
1082 # Revision 1.45 2005/10/04 13:09:49 sjtan
1083 # correct syntax errors; get soap entry working again.
1084 #
1085 # Revision 1.44 2005/09/28 21:27:30 ncq
1086 # - a lot of wx2.6-ification
1087 #
1088 # Revision 1.43 2005/09/28 15:57:48 ncq
1089 # - a whole bunch of wx.Foo -> wx.Foo
1090 #
1091 # Revision 1.42 2005/09/27 20:44:58 ncq
1092 # - wx.wx* -> wx.*
1093 #
1094 # Revision 1.41 2005/09/26 18:01:50 ncq
1095 # - use proper way to import wx26 vs wx2.4
1096 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES
1097 # - time for fixup
1098 #
1099 # Revision 1.40 2005/09/24 09:17:28 ncq
1100 # - some wx2.6 compatibility fixes
1101 #
1102 # Revision 1.39 2005/09/11 17:30:02 ncq
1103 # - cleanup
1104 #
1105 # Revision 1.38 2005/09/08 16:57:48 ncq
1106 # - smaller font in journal display
1107 #
1108 # Revision 1.37 2005/07/21 21:00:46 ncq
1109 # - cleanup, better strings
1110 #
1111 # Revision 1.36 2005/07/02 18:20:52 ncq
1112 # - quite some cleanup
1113 #
1114 # Revision 1.35 2005/06/29 18:35:17 cfmoro
1115 # create encounter from EMR tree. Added FIXME for refactor duplicated code
1116 #
1117 # Revision 1.34 2005/06/29 12:53:50 cfmoro
1118 # Added create issue menu item to root node
1119 #
1120 # Revision 1.33 2005/06/23 14:59:43 ncq
1121 # - cleanup __relink_encounter_data2episode()
1122 #
1123 # Revision 1.32 2005/06/20 13:03:38 cfmoro
1124 # Relink encounter to another episode
1125 #
1126 # Revision 1.31 2005/06/15 22:27:20 ncq
1127 # - allow issue renaming
1128 #
1129 # Revision 1.30 2005/06/14 20:26:04 cfmoro
1130 # refresh tree on unit test startup
1131 #
1132 # Revision 1.29 2005/06/14 20:14:16 cfmoro
1133 # unit testing fix
1134 #
1135 # Revision 1.28 2005/06/14 18:57:50 ncq
1136 # - support renaming an episode
1137 #
1138 # Revision 1.27 2005/04/24 14:44:05 ncq
1139 # - callbacks must be _* not __* or else namespace invisibility will ensue
1140 #
1141 # Revision 1.26 2005/04/12 16:19:49 ncq
1142 # - add cEMRJournalPanel for plugin
1143 #
1144 # Revision 1.25 2005/04/05 16:21:54 ncq
1145 # - a fix by Syan
1146 # - cleanup
1147 #
1148 # Revision 1.24 2005/04/03 20:10:51 ncq
1149 # - add export_emr_to_ascii()
1150 #
1151 # Revision 1.23 2005/04/03 09:15:39 ncq
1152 # - roll back EMR export button
1153 # - my suggestion to place it there wasn't logically sound
1154 # and it screwed up changing the right hand window, too
1155 #
1156 # Revision 1.22 2005/04/02 21:37:27 cfmoro
1157 # Unlinked episodes displayes in EMR tree and dump
1158 #
1159 # Revision 1.21 2005/04/02 20:45:14 cfmoro
1160 # Implementated exporting emr from gui client
1161 #
1162 # Revision 1.20 2005/03/30 22:10:07 ncq
1163 # - just cleanup
1164 #
1165 # Revision 1.19 2005/03/30 18:59:03 cfmoro
1166 # Added file selector dialog to emr dump callback function
1167 #
1168 # Revision 1.18 2005/03/30 18:14:56 cfmoro
1169 # Added emr export button
1170 #
1171 # Revision 1.17 2005/03/29 07:27:14 ncq
1172 # - add missing argument
1173 #
1174 # Revision 1.16 2005/03/11 22:52:54 ncq
1175 # - simplify popup menu use
1176 #
1177 # Revision 1.15 2005/03/10 19:51:29 cfmoro
1178 # Obtained problem from cClinicalRecord on progress notes edition
1179 #
1180 # Revision 1.14 2005/03/09 20:00:13 cfmoro
1181 # Added fixme comment in problem retrieval
1182 #
1183 # Revision 1.13 2005/03/09 19:43:21 cfmoro
1184 # EMR browser edit problem-episodes notes responsible for providing the narrative definitions to cSoapResizingPanel
1185 #
1186 # Revision 1.12 2005/03/09 18:31:57 cfmoro
1187 # As proof of concept: episode editor and notes editor are displayed in the right panel. Just an initial draft, needs feeback a lot of coding yet ;)
1188 #
1189 # Revision 1.11 2005/03/09 16:58:09 cfmoro
1190 # Thanks to Syan code, added contextual menu to emr tree. Linked episode edition action with the responsible dialog
1191 #
1192 # Revision 1.10 2005/02/03 20:19:16 ncq
1193 # - get_demographic_record() -> get_identity()
1194 #
1195 # Revision 1.9 2005/02/01 10:16:07 ihaywood
1196 # refactoring of gmDemographicRecord and follow-on changes as discussed.
1197 #
1198 # gmTopPanel moves to gmHorstSpace
1199 # gmRichardSpace added -- example code at present, haven't even run it myself
1200 # (waiting on some icon .pngs from Richard)
1201 #
1202 # Revision 1.8 2005/01/31 13:02:18 ncq
1203 # - use ask_for_patient() in gmPerson.py
1204 #
1205 # Revision 1.7 2005/01/31 10:37:26 ncq
1206 # - gmPatient.py -> gmPerson.py
1207 #
1208 # Revision 1.6 2004/10/31 00:37:13 cfmoro
1209 # Fixed some method names. Refresh function made public for easy reload, eg. standalone. Refresh browser at startup in standalone mode
1210 #
1211 # Revision 1.5 2004/09/06 18:57:27 ncq
1212 # - Carlos pluginized the lot ! :-)
1213 # - plus some fixes/tabified it
1214 #
1215 # Revision 1.4 2004/09/01 22:01:45 ncq
1216 # - actually use Carlos' issue/episode summary code
1217 #
1218 # Revision 1.3 2004/08/11 09:46:24 ncq
1219 # - now that EMR exporter supports SOAP notes - display them
1220 #
1221 # Revision 1.2 2004/07/26 00:09:27 ncq
1222 # - Carlos brings us data display for the encounters - can REALLY browse EMR now !
1223 #
1224 # Revision 1.1 2004/07/21 12:30:25 ncq
1225 # - initial checkin
1226 #
1227
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 28 04:12:19 2010 | http://epydoc.sourceforge.net |