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