1 """GNUmed patient EMR tree browser.
2 """
3
4 __version__ = "$Revision: 1.111 $"
5 __author__ = "cfmoro1976@yahoo.es, sjtan@swiftdsl.com.au, Karsten.Hilbert@gmx.net"
6 __license__ = "GPL"
7
8
9 import sys, os.path, StringIO, codecs, logging
10
11
12
13 import wx
14
15
16
17 from Gnumed.pycommon import gmI18N, gmDispatcher, gmExceptions, gmTools
18 from Gnumed.exporters import gmPatientExporter
19 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmPersonSearch
20 from Gnumed.wxpython import gmGuiHelpers, gmEMRStructWidgets, gmSOAPWidgets
21 from Gnumed.wxpython import gmAllergyWidgets, gmNarrativeWidgets, gmPatSearchWidgets
22 from Gnumed.wxpython import gmDemographicsWidgets, gmVaccWidgets
23
24
25 _log = logging.getLogger('gm.ui')
26 _log.info(__version__)
27
28
30 """
31 Dump the patient's EMR from GUI client
32 @param parent - The parent widget
33 @type parent - A wx.Window instance
34 """
35
36 if parent is None:
37 raise TypeError('expected wx.Window instance as parent, got <None>')
38
39 pat = gmPerson.gmCurrentPatient()
40 if not pat.connected:
41 gmDispatcher.send(signal='statustext', msg=_('Cannot export EMR. No active patient.'))
42 return False
43
44
45 wc = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
46 defdir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])))
47 gmTools.mkdir(defdir)
48 fname = '%s-%s_%s.txt' % (_('emr-export'), pat['lastnames'], pat['firstnames'])
49 dlg = wx.FileDialog (
50 parent = parent,
51 message = _("Save patient's EMR as..."),
52 defaultDir = defdir,
53 defaultFile = fname,
54 wildcard = wc,
55 style = wx.SAVE
56 )
57 choice = dlg.ShowModal()
58 fname = dlg.GetPath()
59 dlg.Destroy()
60 if choice != wx.ID_OK:
61 return None
62
63 _log.debug('exporting EMR to [%s]', fname)
64
65
66 output_file = codecs.open(fname, 'wb', encoding='utf8', errors='replace')
67 exporter = gmPatientExporter.cEmrExport(patient = pat)
68 exporter.set_output_file(output_file)
69 exporter.dump_constraints()
70 exporter.dump_demographic_record(True)
71 exporter.dump_clinical_record()
72 exporter.dump_med_docs()
73 output_file.close()
74
75 gmDispatcher.send('statustext', msg = _('EMR successfully exported to file: %s') % fname, beep = False)
76 return fname
77
78 -class cEMRTree(wx.TreeCtrl, gmGuiHelpers.cTreeExpansionHistoryMixin):
79 """This wx.TreeCtrl derivative displays a tree view of the medical record."""
80
81
82 - def __init__(self, parent, id, *args, **kwds):
83 """Set up our specialised tree.
84 """
85 kwds['style'] = wx.TR_HAS_BUTTONS | wx.NO_BORDER | wx.TR_SINGLE
86 wx.TreeCtrl.__init__(self, parent, id, *args, **kwds)
87
88 gmGuiHelpers.cTreeExpansionHistoryMixin.__init__(self)
89
90 self.__details_display = None
91 self.__details_display_mode = u'details'
92 self.__enable_display_mode_selection = 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
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
115 self.__details_display = narrative_display
116
118 self.__img_display = image_display
119
121 if not callable(callback):
122 raise ValueError('callback [%s] not callable' % callback)
123
124 self.__enable_display_mode_selection = callback
125
126
127
129 """Configures enabled event signals."""
130 wx.EVT_TREE_SEL_CHANGED (self, self.GetId(), self._on_tree_item_selected)
131 wx.EVT_TREE_ITEM_RIGHT_CLICK (self, self.GetId(), self._on_tree_item_right_clicked)
132
133
134
135 wx.EVT_TREE_ITEM_GETTOOLTIP(self, -1, self._on_tree_item_gettooltip)
136
137 gmDispatcher.connect(signal = 'narrative_mod_db', receiver = self._on_narrative_mod_db)
138 gmDispatcher.connect(signal = 'episode_mod_db', receiver = self._on_episode_mod_db)
139 gmDispatcher.connect(signal = 'health_issue_mod_db', receiver = self._on_issue_mod_db)
140 gmDispatcher.connect(signal = 'family_history_mod_db', receiver = self._on_issue_mod_db)
141
143 """Updates EMR browser data."""
144
145
146
147 wx.BeginBusyCursor()
148
149
150
151
152 self.DeleteAllItems()
153 root_item = self.AddRoot(_('EMR of %(lastnames)s, %(firstnames)s') % self.__pat.get_active_name())
154 self.SetPyData(root_item, None)
155 self.SetItemHasChildren(root_item, True)
156 self.__root_tooltip = self.__pat['description_gender'] + u'\n'
157 if self.__pat['deceased'] is None:
158 self.__root_tooltip += u' %s %s (%s)\n\n' % (
159 gmPerson.map_gender2symbol[self.__pat['gender']],
160 self.__pat.get_formatted_dob(format = '%d %b %Y', encoding = gmI18N.get_encoding()),
161 self.__pat['medical_age']
162 )
163 else:
164 template = u' %s %s - %s (%s)\n\n'
165 self.__root_tooltip += template % (
166 gmPerson.map_gender2symbol[self.__pat['gender']],
167 self.__pat.get_formatted_dob(format = '%d.%b %Y', encoding = gmI18N.get_encoding()),
168 self.__pat['deceased'].strftime('%d.%b %Y').decode(gmI18N.get_encoding()),
169 self.__pat['medical_age']
170 )
171 self.__root_tooltip += gmTools.coalesce(self.__pat['comment'], u'', u'%s\n\n')
172 doc = self.__pat.primary_provider
173 if doc is not None:
174 self.__root_tooltip += u'%s:\n' % _('Primary provider in this praxis')
175 self.__root_tooltip += u' %s %s %s (%s)%s\n\n' % (
176 gmTools.coalesce(doc['title'], gmPerson.map_gender2salutation(gender = doc['gender'])),
177 doc['firstnames'],
178 doc['lastnames'],
179 doc['short_alias'],
180 gmTools.bool2subst(doc['is_active'], u'', u' [%s]' % _('inactive'))
181 )
182 if not ((self.__pat['emergency_contact'] is None) and (self.__pat['pk_emergency_contact'] is None)):
183 self.__root_tooltip += _('In case of emergency contact:') + u'\n'
184 if self.__pat['emergency_contact'] is not None:
185 self.__root_tooltip += gmTools.wrap (
186 text = u'%s\n' % self.__pat['emergency_contact'],
187 width = 60,
188 initial_indent = u' ',
189 subsequent_indent = u' '
190 )
191 if self.__pat['pk_emergency_contact'] is not None:
192 contact = self.__pat.emergency_contact_in_database
193 self.__root_tooltip += u' %s\n' % contact['description_gender']
194 self.__root_tooltip = self.__root_tooltip.strip('\n')
195 if self.__root_tooltip == u'':
196 self.__root_tooltip = u' '
197
198
199 self.__exporter.get_historical_tree(self)
200 self.__curr_node = root_item
201
202 self.SelectItem(root_item)
203 self.Expand(root_item)
204 self.__update_text_for_selected_node()
205
206
207
208 wx.EndBusyCursor()
209 return True
210
212 """Displays information for the selected tree node."""
213
214 if self.__details_display is None:
215 self.__img_display.clear()
216 return
217
218 if self.__curr_node is None:
219 self.__img_display.clear()
220 return
221
222 node_data = self.GetPyData(self.__curr_node)
223 doc_folder = self.__pat.get_document_folder()
224
225 if isinstance(node_data, gmEMRStructItems.cHealthIssue):
226 self.__enable_display_mode_selection(True)
227 if self.__details_display_mode == u'details':
228 txt = node_data.format(left_margin=1, patient = self.__pat)
229 else:
230 txt = node_data.format_as_journal(left_margin = 1)
231
232 self.__img_display.refresh (
233 document_folder = doc_folder,
234 episodes = [ epi['pk_episode'] for epi in node_data.episodes ]
235 )
236
237 elif isinstance(node_data, type({})):
238 self.__enable_display_mode_selection(False)
239
240 txt = _('Pool of unassociated episodes:\n\n "%s"') % node_data['description']
241 self.__img_display.clear()
242
243 elif isinstance(node_data, gmEMRStructItems.cEpisode):
244 self.__enable_display_mode_selection(True)
245 if self.__details_display_mode == u'details':
246 txt = node_data.format(left_margin = 1, patient = self.__pat)
247 else:
248 txt = node_data.format_as_journal(left_margin = 1)
249 self.__img_display.refresh (
250 document_folder = doc_folder,
251 episodes = [node_data['pk_episode']]
252 )
253
254 elif isinstance(node_data, gmEMRStructItems.cEncounter):
255 self.__enable_display_mode_selection(False)
256 epi = self.GetPyData(self.GetItemParent(self.__curr_node))
257 txt = node_data.format (
258 episodes = [epi['pk_episode']],
259 with_soap = True,
260 left_margin = 1,
261 patient = self.__pat,
262 with_co_encountlet_hints = True
263 )
264 self.__img_display.refresh (
265 document_folder = doc_folder,
266 episodes = [epi['pk_episode']],
267 encounter = node_data['pk_encounter']
268 )
269
270
271 else:
272 self.__enable_display_mode_selection(False)
273 emr = self.__pat.get_emr()
274 txt = emr.format_summary(dob = self.__pat['dob'])
275 self.__img_display.clear()
276
277 self.__details_display.Clear()
278 self.__details_display.WriteText(txt)
279 self.__details_display.ShowPosition(0)
280
282
283
284 self.__epi_context_popup = wx.Menu(title = _('Episode Actions:'))
285
286 menu_id = wx.NewId()
287 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Edit details')))
288 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__edit_episode)
289
290 menu_id = wx.NewId()
291 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Delete')))
292 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__delete_episode)
293
294 menu_id = wx.NewId()
295 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Promote')))
296 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__promote_episode_to_issue)
297
298 menu_id = wx.NewId()
299 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Move encounters')))
300 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__move_encounters)
301
302
303 self.__enc_context_popup = wx.Menu(title = _('Encounter Actions:'))
304
305 menu_id = wx.NewId()
306 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Move data to another episode')))
307 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__relink_encounter_data2episode)
308
309 menu_id = wx.NewId()
310 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Edit details')))
311 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__edit_encounter_details)
312
313 item = self.__enc_context_popup.Append(-1, _('Edit progress notes'))
314 self.Bind(wx.EVT_MENU, self.__edit_progress_notes, item)
315
316 item = self.__enc_context_popup.Append(-1, _('Move progress notes'))
317 self.Bind(wx.EVT_MENU, self.__move_progress_notes, item)
318
319 item = self.__enc_context_popup.Append(-1, _('Export for Medistar'))
320 self.Bind(wx.EVT_MENU, self.__export_encounter_for_medistar, item)
321
322
323 self.__issue_context_popup = wx.Menu(title = _('Health Issue Actions:'))
324
325 menu_id = wx.NewId()
326 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Edit details')))
327 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__edit_issue)
328
329 menu_id = wx.NewId()
330 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Delete')))
331 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__delete_issue)
332
333 self.__issue_context_popup.AppendSeparator()
334
335 menu_id = wx.NewId()
336 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Open to encounter level')))
337 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__expand_issue_to_encounter_level)
338
339
340
341
342 self.__root_context_popup = wx.Menu(title = _('EMR Actions:'))
343
344 menu_id = wx.NewId()
345 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Create health issue')))
346 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__create_issue)
347
348 menu_id = wx.NewId()
349 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage allergies')))
350 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__document_allergy)
351
352 menu_id = wx.NewId()
353 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage vaccinations')))
354 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_vaccinations)
355
356 menu_id = wx.NewId()
357 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage procedures')))
358 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_procedures)
359
360 menu_id = wx.NewId()
361 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage hospitalizations')))
362 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_hospital_stays)
363
364 menu_id = wx.NewId()
365 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('Manage occupation')))
366 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__manage_occupation)
367
368 self.__root_context_popup.AppendSeparator()
369
370
371 expand_menu = wx.Menu()
372 self.__root_context_popup.AppendMenu(wx.NewId(), _('Open EMR to ...'), expand_menu)
373
374 menu_id = wx.NewId()
375 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... issue level')))
376 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_issue_level)
377
378 menu_id = wx.NewId()
379 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... episode level')))
380 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_episode_level)
381
382 menu_id = wx.NewId()
383 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... encounter level')))
384 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_encounter_level)
385
386 - def __handle_root_context(self, pos=wx.DefaultPosition):
387 self.PopupMenu(self.__root_context_popup, pos)
388
389 - def __handle_issue_context(self, pos=wx.DefaultPosition):
390
391 self.PopupMenu(self.__issue_context_popup, pos)
392
393 - def __handle_episode_context(self, pos=wx.DefaultPosition):
394
395 self.PopupMenu(self.__epi_context_popup, pos)
396
397 - def __handle_encounter_context(self, pos=wx.DefaultPosition):
398 self.PopupMenu(self.__enc_context_popup, pos)
399
400
401
410
413
417
419 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
420 parent = self,
421 id = -1,
422 caption = _('Deleting episode'),
423 button_defs = [
424 {'label': _('Yes, delete'), 'tooltip': _('Delete the episode if possible (it must be completely empty).')},
425 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the episode.')}
426 ],
427 question = _(
428 'Are you sure you want to delete this episode ?\n'
429 '\n'
430 ' "%s"\n'
431 ) % self.__curr_node_data['description']
432 )
433 result = dlg.ShowModal()
434 if result != wx.ID_YES:
435 return
436
437 try:
438 gmEMRStructItems.delete_episode(episode = self.__curr_node_data)
439 except gmExceptions.DatabaseObjectInUseError:
440 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete episode. There is still clinical data recorded for it.'))
441 return
442
443
444
455
457 encounter = self.GetPyData(self.__curr_node)
458 node_parent = self.GetItemParent(self.__curr_node)
459 episode = self.GetPyData(node_parent)
460
461 gmNarrativeWidgets.manage_progress_notes (
462 parent = self,
463 encounters = [encounter['pk_encounter']],
464 episodes = [episode['pk_episode']]
465 )
466
471
473
474 node_parent = self.GetItemParent(self.__curr_node)
475 owning_episode = self.GetPyData(node_parent)
476
477 episode_selector = gmNarrativeWidgets.cMoveNarrativeDlg (
478 self,
479 -1,
480 episode = owning_episode,
481 encounter = self.__curr_node_data
482 )
483
484 result = episode_selector.ShowModal()
485 episode_selector.Destroy()
486
487 if result == wx.ID_YES:
488 self.__populate_tree()
489
490
491
494
496 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
497 parent = self,
498 id = -1,
499 caption = _('Deleting health issue'),
500 button_defs = [
501 {'label': _('Yes, delete'), 'tooltip': _('Delete the health issue if possible (it must be completely empty).')},
502 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the health issue.')}
503 ],
504 question = _(
505 'Are you sure you want to delete this health issue ?\n'
506 '\n'
507 ' "%s"\n'
508 ) % self.__curr_node_data['description']
509 )
510 result = dlg.ShowModal()
511 if result != wx.ID_YES:
512 dlg.Destroy()
513 return
514
515 dlg.Destroy()
516
517 try:
518 gmEMRStructItems.delete_health_issue(health_issue = self.__curr_node_data)
519 except gmExceptions.DatabaseObjectInUseError:
520 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete health issue. There is still clinical data recorded for it.'))
521
523
524 if not self.__curr_node.IsOk():
525 return
526
527 self.Expand(self.__curr_node)
528
529 epi, epi_cookie = self.GetFirstChild(self.__curr_node)
530 while epi.IsOk():
531 self.Expand(epi)
532 epi, epi_cookie = self.GetNextChild(self.__curr_node, epi_cookie)
533
534
535
538
546
549
552
555
558
560
561 root_item = self.GetRootItem()
562
563 if not root_item.IsOk():
564 return
565
566 self.Expand(root_item)
567
568
569 issue, issue_cookie = self.GetFirstChild(root_item)
570 while issue.IsOk():
571 self.Collapse(issue)
572 epi, epi_cookie = self.GetFirstChild(issue)
573 while epi.IsOk():
574 self.Collapse(epi)
575 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
576 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
577
579
580 root_item = self.GetRootItem()
581
582 if not root_item.IsOk():
583 return
584
585 self.Expand(root_item)
586
587
588 issue, issue_cookie = self.GetFirstChild(root_item)
589 while issue.IsOk():
590 self.Expand(issue)
591 epi, epi_cookie = self.GetFirstChild(issue)
592 while epi.IsOk():
593 self.Collapse(epi)
594 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
595 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
596
598
599 root_item = self.GetRootItem()
600
601 if not root_item.IsOk():
602 return
603
604 self.Expand(root_item)
605
606
607 issue, issue_cookie = self.GetFirstChild(root_item)
608 while issue.IsOk():
609 self.Expand(issue)
610 epi, epi_cookie = self.GetFirstChild(issue)
611 while epi.IsOk():
612 self.Expand(epi)
613 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
614 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
615
622
623
624
626 wx.CallAfter(self.__update_text_for_selected_node)
627
629 wx.CallAfter(self.__populate_tree)
630
632 wx.CallAfter(self.__populate_tree)
633
635 sel_item = event.GetItem()
636 self.__curr_node = sel_item
637 self.__update_text_for_selected_node()
638 return True
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
778
779
780
781
782
783
784
785
786
787
788
790 """Right button clicked: display the popup for the tree"""
791
792 node = event.GetItem()
793 self.SelectItem(node)
794 self.__curr_node_data = self.GetPyData(node)
795 self.__curr_node = node
796
797 pos = wx.DefaultPosition
798 if isinstance(self.__curr_node_data, gmEMRStructItems.cHealthIssue):
799 self.__handle_issue_context(pos=pos)
800 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEpisode):
801 self.__handle_episode_context(pos=pos)
802 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEncounter):
803 self.__handle_encounter_context(pos=pos)
804 elif node == self.GetRootItem():
805 self.__handle_root_context()
806 elif type(self.__curr_node_data) == type({}):
807
808 pass
809 else:
810 print "error: unknown node type, no popup menu"
811 event.Skip()
812
814 """Used in sorting items.
815
816 -1: 1 < 2
817 0: 1 = 2
818 1: 1 > 2
819 """
820
821
822 item1 = self.GetPyData(node1)
823 item2 = self.GetPyData(node2)
824
825
826 if isinstance(item1, type({})):
827 return -1
828 if isinstance(item2, type({})):
829 return 1
830
831
832 if isinstance(item1, gmEMRStructItems.cEncounter):
833 if item1['started'] == item2['started']:
834 return 0
835 if item1['started'] > item2['started']:
836 return -1
837 return 1
838
839
840 if isinstance(item1, gmEMRStructItems.cEpisode):
841 start1 = item1.get_access_range()[0]
842 start2 = item2.get_access_range()[0]
843 if start1 == start2:
844 return 0
845 if start1 < start2:
846 return -1
847 return 1
848
849
850 if isinstance(item1, gmEMRStructItems.cHealthIssue):
851
852
853 if item1['grouping'] is None:
854 if item2['grouping'] is not None:
855 return 1
856
857
858 if item1['grouping'] is not None:
859 if item2['grouping'] is None:
860 return -1
861
862
863 if (item1['grouping'] is None) and (item2['grouping'] is None):
864 if item1['description'].lower() < item2['description'].lower():
865 return -1
866 if item1['description'].lower() > item2['description'].lower():
867 return 1
868 return 0
869
870
871 if item1['grouping'] < item2['grouping']:
872 return -1
873
874 if item1['grouping'] > item2['grouping']:
875 return 1
876
877 if item1['description'].lower() < item2['description'].lower():
878 return -1
879
880 if item1['description'].lower() > item2['description'].lower():
881 return 1
882
883 return 0
884
885 _log.error('unknown item type during sorting EMR tree:')
886 _log.error('item1: %s', type(item1))
887 _log.error('item2: %s', type(item2))
888
889 return 0
890
891
892
894 return self.__details_display_mode
895
897 if mode not in [u'details', u'journal']:
898 raise ValueError('details display mode must be one of "details", "journal"')
899 if self.__details_display_mode == mode:
900 return
901 self.__details_display_mode = mode
902 self.__update_text_for_selected_node()
903
904 details_display_mode = property(_get_details_display_mode, _set_details_display_mode)
905
906 from Gnumed.wxGladeWidgets import wxgScrolledEMRTreePnl
907
921
922 from Gnumed.wxGladeWidgets import wxgSplittedEMRTreeBrowserPnl
923
925 """A splitter window holding an EMR tree.
926
927 The left hand side displays a scrollable EMR tree while
928 on the right details for selected items are displayed.
929
930 Expects to be put into a Notebook.
931 """
938
940 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
941 return True
942
943
944
946 if self.GetParent().GetCurrentPage() == self:
947 self.repopulate_ui()
948 return True
949
953
957
958
959
961 """Fills UI with data."""
962 self._pnl_emr_tree.repopulate_ui()
963 self._splitter_browser.SetSashPosition(self._splitter_browser.GetSizeTuple()[0]/3, True)
964 return True
965
967 if enable:
968 self._RBTN_details.Enable(True)
969 self._RBTN_journal.Enable(True)
970 return
971 self._RBTN_details.Enable(False)
972 self._RBTN_journal.Enable(False)
973
976 wx.Panel.__init__(self, *args, **kwargs)
977
978 self.__do_layout()
979 self.__register_events()
980
982 self.__journal = wx.TextCtrl (
983 self,
984 -1,
985 _('No EMR data loaded.'),
986 style = wx.TE_MULTILINE | wx.TE_READONLY
987 )
988 self.__journal.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL))
989
990 szr_outer = wx.BoxSizer(wx.VERTICAL)
991 szr_outer.Add(self.__journal, 1, wx.EXPAND, 0)
992
993 self.SetAutoLayout(1)
994 self.SetSizer(szr_outer)
995 szr_outer.Fit(self)
996 szr_outer.SetSizeHints(self)
997 self.Layout()
998
1000 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1001
1003 """Expects to be in a Notebook."""
1004 if self.GetParent().GetCurrentPage() == self:
1005 self.repopulate_ui()
1006 return True
1007
1008
1009
1011 txt = StringIO.StringIO()
1012 exporter = gmPatientExporter.cEMRJournalExporter()
1013
1014
1015 try:
1016 exporter.export(txt)
1017 self.__journal.SetValue(txt.getvalue())
1018 except ValueError:
1019 _log.exception('cannot get EMR journal')
1020 self.__journal.SetValue (_(
1021 'An error occurred while retrieving the EMR\n'
1022 'in journal form for the active patient.\n\n'
1023 'Please check the log file for details.'
1024 ))
1025 txt.close()
1026 self.__journal.ShowPosition(self.__journal.GetLastPosition())
1027 return True
1028
1029
1030
1031 if __name__ == '__main__':
1032
1033 _log.info("starting emr browser...")
1034
1035 try:
1036
1037 patient = gmPersonSearch.ask_for_patient()
1038 if patient is None:
1039 print "No patient. Exiting gracefully..."
1040 sys.exit(0)
1041 gmPatSearchWidgets.set_active_patient(patient = patient)
1042
1043
1044 application = wx.PyWidgetTester(size=(800,600))
1045 emr_browser = cEMRBrowserPanel(application.frame, -1)
1046 emr_browser.refresh_tree()
1047
1048 application.frame.Show(True)
1049 application.MainLoop()
1050
1051
1052 if patient is not None:
1053 try:
1054 patient.cleanup()
1055 except:
1056 print "error cleaning up patient"
1057 except StandardError:
1058 _log.exception("unhandled exception caught !")
1059
1060 raise
1061
1062 _log.info("closing emr browser...")
1063
1064
1065