| Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed SOAP related widgets.
2 """
3 #============================================================
4 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/wxpython/gmSOAPWidgets.py,v $
5 # $Id: gmSOAPWidgets.py,v 1.114 2010/01/11 19:59:13 ncq Exp $
6 __version__ = "$Revision: 1.114 $"
7 __author__ = "Carlos Moro <cfmoro1976@yahoo.es>, K.Hilbert <Karsten.Hilbert@gmx.net>"
8 __license__ = "GPL"
9
10 # std library
11 import types, logging
12
13
14 # 3rd party
15 import wx
16
17
18 # GNUmed
19 from Gnumed.pycommon import gmDispatcher, gmI18N, gmExceptions, gmMatchProvider, gmTools, gmCfg
20 from Gnumed.wxpython import gmResizingWidgets, gmPhraseWheel, gmEMRStructWidgets, gmGuiHelpers, gmRegetMixin, gmEditArea, gmPatSearchWidgets
21 from Gnumed.business import gmPerson, gmEMRStructItems, gmSOAPimporter, gmSurgery
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26 #============================================================
28 ea = gmEMRStructWidgets.cHealthIssueEditArea (
29 parent,
30 -1,
31 wx.DefaultPosition,
32 wx.DefaultSize,
33 wx.NO_BORDER | wx.TAB_TRAVERSAL,
34 data_sink = data_sink
35 )
36 popup = gmEditArea.cEditAreaPopup (
37 parent = parent,
38 id = -1,
39 title = '',
40 pos = pos,
41 size = size,
42 style = style,
43 name = '',
44 edit_area = ea
45 )
46 return popup
47 #============================================================
49 ea = gmVaccWidgets.cVaccinationEditArea (
50 parent = parent,
51 id = -1,
52 pos = pos,
53 size = size,
54 style = style,
55 data_sink = data_sink
56 )
57 popup = gmEditArea.cEditAreaPopup (
58 parent = parent,
59 id = -1,
60 title = _('Enter vaccination given'),
61 pos = pos,
62 size = size,
63 style = style,
64 name = '',
65 edit_area = ea
66 )
67 return popup
68 #============================================================
69 # FIXME: keywords hardcoded for now, load from cfg in backend instead
70 progress_note_keywords = {
71 's': {
72 '$missing_action': {},
73 'phx$': {
74 'widget_factory': create_issue_popup,
75 'widget_data_sink': None
76 },
77 'ea$:': {
78 'widget_factory': create_issue_popup,
79 'widget_data_sink': None
80 },
81 '$vacc': {
82 'widget_factory': create_vacc_popup,
83 'widget_data_sink': None
84 },
85 'impf:': {
86 'widget_factory': create_vacc_popup,
87 'widget_data_sink': None
88 },
89 'icpc:': {},
90 'icpc?': {}
91 },
92 'o': {
93 'icpc:': {},
94 'icpc?': {}
95 },
96 'a': {
97 'icpc:': {},
98 'icpc?': {}
99 },
100 'p': {
101 '$vacc': {
102 'widget_factory': create_vacc_popup,
103 'widget_data_sink': None
104 },
105 'icpc:': {},
106 'icpc?': {}
107 }
108 }
109 #============================================================
111 """A notebook holding panels with progress note editors.
112
113 There is one progress note editor panel for each episode being worked on.
114 """
116 wx.Notebook.__init__ (
117 self,
118 parent = parent,
119 id = id,
120 pos = pos,
121 size = size,
122 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER,
123 name = self.__class__.__name__
124 )
125 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
126 self.__pat = gmPerson.gmCurrentPatient()
127 self.__do_layout()
128 self.__register_interests()
129 #--------------------------------------------------------
130 # public API
131 #--------------------------------------------------------
133 """Add a progress note editor page.
134
135 The way <allow_same_problem> is currently used in callers
136 it only applies to unassociated episodes.
137 """
138 problem_to_add = problem
139
140 # determine label
141 if problem_to_add is None:
142 label = _('new problem')
143 else:
144 # normalize problem type
145 emr = self.__pat.get_emr()
146 if isinstance(problem_to_add, gmEMRStructItems.cEpisode):
147 problem_to_add = emr.episode2problem(episode = problem_to_add)
148 elif isinstance(problem_to_add, gmEMRStructItems.cHealthIssue):
149 problem_to_add = emr.health_issue2problem(issue = problem_to_add)
150 if not isinstance(problem_to_add, gmEMRStructItems.cProblem):
151 raise TypeError('cannot open progress note editor for [%s]' % problem_to_add)
152 label = problem_to_add['problem']
153 # FIXME: configure maximum length
154 if len(label) > 23:
155 label = label[:21] + gmTools.u_ellipsis
156
157 if allow_same_problem:
158 new_page = cResizingSoapPanel(parent = self, problem = problem_to_add)
159 result = self.AddPage (
160 page = new_page,
161 text = label,
162 select = True
163 )
164 return result
165
166 # check for dupes
167 # new unassociated problem
168 if problem_to_add is None:
169 # check for dupes
170 for page_idx in range(self.GetPageCount()):
171 page = self.GetPage(page_idx)
172 # found
173 if page.get_problem() is None:
174 self.SetSelection(page_idx)
175 return True
176 continue
177 # not found
178 new_page = cResizingSoapPanel(parent = self, problem = problem_to_add)
179 result = self.AddPage (
180 page = new_page,
181 text = label,
182 select = True
183 )
184 return result
185
186 # real problem
187 # - raise existing editor ?
188 for page_idx in range(self.GetPageCount()):
189 page = self.GetPage(page_idx)
190 problem_of_page = page.get_problem()
191 # editor is for unassociated new problem
192 if problem_of_page is None:
193 continue
194 # editor is for episode
195 if problem_of_page['type'] == 'episode':
196 if problem_to_add['type'] == 'issue':
197 is_equal = (problem_of_page['pk_health_issue'] == problem_to_add['pk_health_issue'])
198 else:
199 is_equal = (problem_of_page['pk_episode'] == problem_to_add['pk_episode'])
200 if is_equal:
201 self.SetSelection(page_idx)
202 return True
203 continue
204 # editor is for health issue
205 if problem_of_page['type'] == 'issue':
206 if problem_of_page['pk_health_issue'] == problem_to_add['pk_health_issue']:
207 self.SetSelection(page_idx)
208 return True
209 continue
210
211 # - add new editor
212 new_page = cResizingSoapPanel(parent = self, problem = problem_to_add)
213 result = self.AddPage (
214 page = new_page,
215 text = label,
216 select = True
217 )
218
219 return result
220 #--------------------------------------------------------
222
223 page_idx = self.GetSelection()
224 page = self.GetPage(page_idx)
225
226 if not page.editor_empty():
227 really_discard = gmGuiHelpers.gm_show_question (
228 _('Are you sure you really want to\n'
229 'discard this progress note ?\n'
230 ),
231 _('Discarding progress note')
232 )
233 if really_discard is False:
234 return
235
236 self.DeletePage(page_idx)
237
238 # always keep one unassociated editor open
239 if self.GetPageCount() == 0:
240 self.add_editor()
241 #--------------------------------------------------------
243
244 for page_idx in range(self.GetPageCount()):
245 page = self.GetPage(page_idx)
246 if page.editor_empty():
247 continue
248
249 gmGuiHelpers.gm_show_warning (
250 _('There are unsaved progress notes !\n'),
251 _('Unsaved progress notes')
252 )
253 return False
254
255 return True
256 #--------------------------------------------------------
258 save_all = False
259 dlg = None
260 for page_idx in range(self.GetPageCount()):
261 page = self.GetPage(page_idx)
262 if page.editor_empty():
263 continue
264
265 if dlg is None:
266 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
267 self,
268 -1,
269 caption = _('Unsaved progress note'),
270 question = _(
271 'This progress note has not been saved yet.\n'
272 '\n'
273 'Do you want to save it or discard it ?\n\n'
274 ),
275 button_defs = [
276 {'label': _('&Save'), 'tooltip': _('Save this progress note'), 'default': True},
277 {'label': _('&Discard'), 'tooltip': _('Discard this progress note'), 'default': False},
278 {'label': _('Save &all'), 'tooltip': _('Save all remaining unsaved progress notes'), 'default': False}
279 ]
280 )
281
282 if not save_all:
283 self.ChangeSelection(page_idx)
284 decision = dlg.ShowModal()
285 if decision == wx.ID_NO:
286 _log.info('user requested discarding of unsaved progress note')
287 continue
288 if decision == wx.ID_CANCEL:
289 save_all = True
290 page.save()
291
292 if dlg is not None:
293 dlg.Destroy()
294 #--------------------------------------------------------
295 # internal API
296 #--------------------------------------------------------
298 # add one empty unassociated progress note editor - which to
299 # have (by all sensible accounts) seems to be the intent when
300 # instantiating this class
301 self.add_editor()
302 #--------------------------------------------------------
303 # reget mixin API
304 #--------------------------------------------------------
306 print '[%s._populate_with_data] nothing to do, really...' % self.__class__.__name__
307 return True
308 #--------------------------------------------------------
309 # event handling
310 #--------------------------------------------------------
312 """Configure enabled event signals
313 """
314 # wxPython events
315
316 # client internal signals
317 gmDispatcher.connect(signal = u'post_patient_selection', receiver=self._on_post_patient_selection)
318 # gmDispatcher.connect(signal = u'application_closing', receiver=self._on_application_closing)
319
320 self.__pat.register_pre_selection_callback(callback = self._pre_selection_callback)
321
322 gmDispatcher.send(signal = u'register_pre_exit_callback', callback = self._pre_exit_callback)
323 #--------------------------------------------------------
325 """Another patient is about to be activated.
326
327 Patient change will not proceed before this returns True.
328 """
329 return self.warn_on_unsaved_soap()
330 #--------------------------------------------------------
332 """The client is about to be shut down.
333
334 Shutdown will not proceed before this returns.
335 """
336 self.save_unsaved_soap()
337 #--------------------------------------------------------
339 """Patient changed."""
340 self.DeleteAllPages()
341 self.add_editor()
342 self._schedule_data_reget()
343 #--------------------------------------------------------
344 # def _on_application_closing(self):
345 # """GNUmed is shutting down."""
346 # print "[%s]: the application is closing down" % self.__class__.__name__
347 # print "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
348 # print "need to ask user about SOAP saving !"
349 # print "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
350 #--------------------------------------------------------
351 # def _on_episodes_modified(self):
352 # print "[%s]: episode modified" % self.__class__.__name__
353 # print "need code to deal with:"
354 # print "- deleted episode that we show so we can notify the user"
355 # print "- renamed episode so we can update our episode label"
356 # self._schedule_data_reget()
357 # pass
358 #============================================================
360 """A panel for entering multiple progress notes in context.
361
362 Expects to be used as a notebook page.
363
364 Left hand side:
365 - problem list (health issues and active episodes)
366
367 Right hand side:
368 - notebook with progress note editors
369
370 Listens to patient change signals, thus acts on the current patient.
371 """
372 #--------------------------------------------------------
374 """Contructs a new instance of SOAP input panel
375
376 @param parent: Wx parent widget
377 @param id: Wx widget id
378 """
379 # Call parents constructors
380 wx.Panel.__init__ (
381 self,
382 parent = parent,
383 id = id,
384 pos = wx.DefaultPosition,
385 size = wx.DefaultSize,
386 style = wx.NO_BORDER
387 )
388 self.__pat = gmPerson.gmCurrentPatient()
389
390 # ui contruction and event handling set up
391 self.__do_layout()
392 self.__register_interests()
393 self.reset_ui_content()
394 #--------------------------------------------------------
395 # public API
396 #--------------------------------------------------------
398 """
399 Clear all information from input panel
400 """
401 self.__LST_problems.Clear()
402 self.__soap_notebook.DeleteAllPages()
403 self.__soap_notebook.add_editor()
404 #--------------------------------------------------------
405 # internal helpers
406 #--------------------------------------------------------
408 """Arrange widgets.
409
410 left: problem list (mix of issues and episodes)
411 right: soap editors
412 """
413 # SOAP input panel main splitter window
414 self.__splitter = wx.SplitterWindow(self, -1)
415
416 # left hand side
417 PNL_list = wx.Panel(self.__splitter, -1)
418 # - header
419 list_header = wx.StaticText (
420 parent = PNL_list,
421 id = -1,
422 label = _('Active problems'),
423 style = wx.NO_BORDER | wx.ALIGN_CENTRE
424 )
425 # - problem list
426 self.__LST_problems = wx.ListBox (
427 PNL_list,
428 -1,
429 style= wx.NO_BORDER
430 )
431 # - arrange
432 szr_left = wx.BoxSizer(wx.VERTICAL)
433 szr_left.Add(list_header, 0)
434 szr_left.Add(self.__LST_problems, 1, wx.EXPAND)
435 PNL_list.SetSizerAndFit(szr_left)
436
437 # right hand side
438 # - soap inputs panel
439 PNL_soap_editors = wx.Panel(self.__splitter, -1)
440 # - progress note notebook
441 self.__soap_notebook = cProgressNoteInputNotebook(PNL_soap_editors, -1)
442 # - buttons
443 self.__BTN_add_unassociated = wx.Button(PNL_soap_editors, -1, _('&New'))
444 tt = _(
445 'Add editor for a new unassociated progress note.\n\n'
446 'There is a configuration option whether or not to\n'
447 'allow several new unassociated progress notes at once.'
448 )
449 self.__BTN_add_unassociated.SetToolTipString(tt)
450
451 self.__BTN_save = wx.Button(PNL_soap_editors, -1, _('&Save'))
452 self.__BTN_save.SetToolTipString(_('Save progress note into medical record and close this editor.'))
453
454 self.__BTN_clear = wx.Button(PNL_soap_editors, -1, _('&Clear'))
455 self.__BTN_clear.SetToolTipString(_('Clear this progress note editor.'))
456
457 self.__BTN_discard = wx.Button(PNL_soap_editors, -1, _('&Discard'))
458 self.__BTN_discard.SetToolTipString(_('Discard progress note and close this editor. You will loose any data already typed into this editor !'))
459
460 # - arrange
461 szr_btns_right = wx.BoxSizer(wx.HORIZONTAL)
462 szr_btns_right.Add(self.__BTN_add_unassociated, 0, wx.SHAPED)
463 szr_btns_right.Add(self.__BTN_clear, 0, wx.SHAPED)
464 szr_btns_right.Add(self.__BTN_save, 0, wx.SHAPED)
465 szr_btns_right.Add(self.__BTN_discard, 0, wx.SHAPED)
466
467 szr_right = wx.BoxSizer(wx.VERTICAL)
468 szr_right.Add(self.__soap_notebook, 1, wx.EXPAND)
469 szr_right.Add(szr_btns_right)
470 PNL_soap_editors.SetSizerAndFit(szr_right)
471
472 # arrange widgets
473 self.__splitter.SetMinimumPaneSize(20)
474 self.__splitter.SplitVertically(PNL_list, PNL_soap_editors)
475
476 szr_main = wx.BoxSizer(wx.VERTICAL)
477 szr_main.Add(self.__splitter, 1, wx.EXPAND, 0)
478 self.SetSizerAndFit(szr_main)
479 #--------------------------------------------------------
481 """Update health problems list.
482 """
483 self.__LST_problems.Clear()
484 emr = self.__pat.get_emr()
485 problems = emr.get_problems()
486 for problem in problems:
487 if not problem['problem_active']:
488 continue
489 if problem['type'] == 'issue':
490 issue = emr.problem2issue(problem)
491 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
492 if last_encounter is None:
493 last = issue['modified_when'].strftime('%m/%Y')
494 else:
495 last = last_encounter['last_affirmed'].strftime('%m/%Y')
496 label = u'%s: %s "%s"' % (last, problem['l10n_type'], problem['problem'])
497 elif problem['type'] == 'episode':
498 epi = emr.problem2episode(problem)
499 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
500 if last_encounter is None:
501 last = epi['episode_modified_when'].strftime('%m/%Y')
502 else:
503 last = last_encounter['last_affirmed'].strftime('%m/%Y')
504 label = u'%s: %s "%s"%s' % (
505 last,
506 problem['l10n_type'],
507 problem['problem'],
508 gmTools.coalesce(initial = epi['health_issue'], instead = '', template_initial = ' (%s)')
509 )
510 self.__LST_problems.Append(label, problem)
511 splitter_width = self.__splitter.GetSizeTuple()[0]
512 self.__splitter.SetSashPosition((splitter_width / 2), True)
513 self.Refresh()
514 #self.Update()
515 return True
516 #--------------------------------------------------------
517 # event handling
518 #--------------------------------------------------------
520 """Configure enabled event signals
521 """
522 # wxPython events
523 wx.EVT_LISTBOX_DCLICK(self.__LST_problems, self.__LST_problems.GetId(), self.__on_problem_activated)
524 wx.EVT_BUTTON(self.__BTN_save, self.__BTN_save.GetId(), self.__on_save)
525 wx.EVT_BUTTON(self.__BTN_clear, self.__BTN_clear.GetId(), self.__on_clear)
526 wx.EVT_BUTTON(self.__BTN_discard, self.__BTN_discard.GetId(), self.__on_discard)
527 wx.EVT_BUTTON(self.__BTN_add_unassociated, self.__BTN_add_unassociated.GetId(), self.__on_add_unassociated)
528
529 # client internal signals
530 gmDispatcher.connect(signal='post_patient_selection', receiver=self._on_post_patient_selection)
531 gmDispatcher.connect(signal = 'episode_mod_db', receiver = self._on_episode_issue_mod_db)
532 gmDispatcher.connect(signal = 'health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
533 #--------------------------------------------------------
535 """Patient changed."""
536 if self.GetParent().GetCurrentPage() == self:
537 self.reset_ui_content()
538 #--------------------------------------------------------
542 #--------------------------------------------------------
544 """Clear raised SOAP input widget.
545 """
546 soap_nb_page = self.__soap_notebook.GetPage(self.__soap_notebook.GetSelection())
547 soap_nb_page.Clear()
548 #--------------------------------------------------------
550 """Discard raised SOAP input widget.
551
552 Will throw away data !
553 """
554 self.__soap_notebook.close_current_editor()
555 #--------------------------------------------------------
557 """Add new editor for as-yet unassociated progress note.
558
559 Clinical logic as per discussion with Jim Busser:
560
561 - if patient has no episodes:
562 - new patient
563 - always allow several NEWs
564 - if patient has episodes:
565 - allow several NEWs per configuration
566 """
567 emr = self.__pat.get_emr()
568 epis = emr.get_episodes()
569
570 if len(epis) == 0:
571 value = True
572 else:
573 dbcfg = gmCfg.cCfgSQL()
574 value = bool(dbcfg.get2 (
575 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
576 workplace = gmSurgery.gmCurrentPractice().active_workplace,
577 bias = u'user',
578 default = False
579 ))
580
581 self.__soap_notebook.add_editor(allow_same_problem = value)
582 #--------------------------------------------------------
584 """
585 When the user changes health issue selection, update selected issue
586 reference and update buttons according its input status.
587
588 when the user selects a problem in the problem list:
589 - check whether selection is issue or episode
590 - if editor for episode exists: focus it
591 - if no editor for episode exists: create one and focus it
592 """
593 problem_idx = self.__LST_problems.GetSelection()
594 problem = self.__LST_problems.GetClientData(problem_idx)
595
596 if self.__soap_notebook.add_editor(problem = problem):
597 return True
598
599 gmGuiHelpers.gm_show_error (
600 aMessage = _(
601 'Cannot open progress note editor for\n\n'
602 '[%s].\n\n'
603 ) % problem['problem'],
604 aTitle = _('opening progress note editor')
605 )
606 return False
607 #--------------------------------------------------------
609 """Save data to backend and close editor.
610 """
611 page_idx = self.__soap_notebook.GetSelection()
612 soap_nb_page = self.__soap_notebook.GetPage(page_idx)
613 if not soap_nb_page.save():
614 gmDispatcher.send(signal='statustext', msg=_('Problem saving progress note: duplicate information ?'))
615 return False
616 self.__soap_notebook.DeletePage(page_idx)
617 # always keep one unassociated editor open
618 self.__soap_notebook.add_editor()
619 #self.__refresh_problem_list()
620 return True
621 #--------------------------------------------------------
622 # notebook plugin API
623 #--------------------------------------------------------
626 #============================================================
634 #============================================================
635 # FIXME: this should be a more generic(ally named) class
636 # FIXME: living elsewhere
638
639 _data_savers = {}
640
643 #--------------------------------------------------------
645 # FIXME: do fancy validations
646
647 print "storing popup data:", desc
648 print "type", popup_type
649 print "data", data
650
651 # verify structure
652 try:
653 self.__data[popup_type]
654 except KeyError:
655 self.__data[popup_type] = {}
656 # store new data
657 self.__data[popup_type][desc] = {
658 'data': data
659 }
660 # remove old data if necessary
661 try:
662 del self.__data[popup_type][old_desc]
663 except:
664 pass
665 return True
666 #--------------------------------------------------------
668 for popup_type in self.__data.keys():
669 try:
670 saver_func = self.__data_savers[popup_type]
671 except KeyError:
672 _log.exception('no saver for popup data type [%s] configured', popup_type)
673 return False
674 for desc in self.__data[popup_type].keys():
675 data = self.__data[popup_type][desc]['data']
676 saver_func(data)
677 return True
678 #--------------------------------------------------------
681 #--------------------------------------------------------
682 # def remove_data(self, popup_type=None, desc=None):
683 # del self.__data[popup_type][desc]
684 #--------------------------------------------------------
685 # def get_descs(self, popup_type=None, origination_soap=None):
686 # def get_data(self, desc=None):
687 # def rename_data(self, old_desc=None, new_desc=None):
688 #============================================================
690
692 """Resizing SOAP note input editor.
693
694 This is a wrapper around a few resizing STCs (the
695 labels and categories are settable) which are
696 customized to accept progress note input. It provides
697 the unified resizing behaviour.
698
699 Knows how to save it's data into the backend.
700
701 @param input_defs: note's labels and categories
702 @type input_defs: list of cSOAPLineDef instances
703 """
704 if input_defs is None or len(input_defs) == 0:
705 raise gmExceptions.ConstructorError, 'cannot generate note with field defs [%s]' % input_defs
706
707 # FIXME: *actually* this should be a session-local
708 # FIXME: holding store at the cClinicalRecord level
709 self.__embedded_data_holder = cPopupDataHolder()
710
711 self.__input_defs = input_defs
712
713 gmResizingWidgets.cResizingWindow.__init__(self, parent, id=-1, size=size)
714
715 self.__problem = problem
716 if isinstance(problem, gmEMRStructItems.cEpisode):
717 self.__problem = emr.episode2problem(episode = problem)
718 elif isinstance(problem, gmEMRStructItems.cHealthIssue):
719 self.__problem = emr.health_issue2problem(issue = problem)
720 self.__pat = gmPerson.gmCurrentPatient()
721 #--------------------------------------------------------
722 # cResizingWindow API
723 #--------------------------------------------------------
725 """Visually display input note according to user defined labels.
726 """
727 # configure keywords
728 for soap_cat in progress_note_keywords.keys():
729 category = progress_note_keywords[soap_cat]
730 for kwd in category.keys():
731 category[kwd]['widget_data_sink'] = self.__embedded_data_holder.store_data
732 input_fields = []
733 # add fields to edit widget
734 # note: this may produce identically labelled lines
735 for line_def in self.__input_defs:
736 input_field = gmResizingWidgets.cResizingSTC(self, -1, data = line_def)
737 input_field.SetText(line_def.text)
738 kwds = progress_note_keywords[line_def.soap_cat]
739 input_field.set_keywords(popup_keywords=kwds)
740 # FIXME: pending matcher setup
741 self.AddWidget(widget=input_field, label=line_def.label)
742 self.Newline()
743 input_fields.append(input_field)
744 # setup tab navigation between input fields
745 for field_idx in range(len(input_fields)):
746 # previous
747 try:
748 input_fields[field_idx].prev_in_tab_order = input_fields[field_idx-1]
749 except IndexError:
750 input_fields[field_idx].prev_in_tab_order = None
751 # next
752 try:
753 input_fields[field_idx].next_in_tab_order = input_fields[field_idx+1]
754 except IndexError:
755 input_fields[field_idx].next_in_tab_order = None
756 #--------------------------------------------------------
757 # public API
758 #--------------------------------------------------------
760 """Save data into backend."""
761
762 # fill progress_note for import
763 progress_note = []
764 aoe = u''
765 rfe = u''
766 has_rfe = False
767 soap_lines_contents = self.GetValue()
768 for line_content in soap_lines_contents.values():
769 if line_content.text.strip() == u'':
770 continue
771 progress_note.append ({
772 gmSOAPimporter.soap_bundle_SOAP_CAT_KEY: line_content.data.soap_cat,
773 gmSOAPimporter.soap_bundle_TYPES_KEY: [], # these types need to come from the editor
774 gmSOAPimporter.soap_bundle_TEXT_KEY: line_content.text.rstrip()
775 })
776 if line_content.data.is_rfe:
777 has_rfe = True
778 rfe += line_content.text.rstrip()
779 if line_content.data.soap_cat == u'a':
780 aoe += line_content.text.rstrip()
781
782 emr = self.__pat.get_emr()
783
784 # - new episode, must get name from narrative (or user)
785 if (self.__problem is None) or (self.__problem['type'] == 'issue'):
786 # work out episode name
787 epi_name = u''
788 if len(aoe) != 0:
789 epi_name = aoe
790 else:
791 epi_name = rfe
792
793 dlg = wx.TextEntryDialog (
794 parent = self,
795 message = _('Enter a descriptive name for this new problem:'),
796 caption = _('Creating a problem (episode) to save the notelet under ...'),
797 defaultValue = epi_name.replace('\r', '//').replace('\n', '//'),
798 style = wx.OK | wx.CANCEL | wx.CENTRE
799 )
800 decision = dlg.ShowModal()
801 if decision != wx.ID_OK:
802 return False
803
804 epi_name = dlg.GetValue().strip()
805 if epi_name == u'':
806 gmGuiHelpers.gm_show_error(_('Cannot save a new problem without a name.'), _('saving progress note'))
807 return False
808
809 # new unassociated episode
810 new_episode = emr.add_episode(episode_name = epi_name[:45], pk_health_issue = None, is_open = True)
811
812 if self.__problem is not None:
813 issue = emr.problem2issue(self.__problem)
814 if not gmEMRStructWidgets.move_episode_to_issue(episode = new_episode, target_issue = issue, save_to_backend = True):
815 print "error moving episode to issue"
816
817 epi_id = new_episode['pk_episode']
818 else:
819 epi_id = self.__problem['pk_episode']
820
821 # set up clinical context in progress note
822 encounter = emr.active_encounter
823 staff_id = gmPerson.gmCurrentProvider()['pk_staff']
824 clin_ctx = {
825 gmSOAPimporter.soap_bundle_EPISODE_ID_KEY: epi_id,
826 gmSOAPimporter.soap_bundle_ENCOUNTER_ID_KEY: encounter['pk_encounter'],
827 gmSOAPimporter.soap_bundle_STAFF_ID_KEY: staff_id
828 }
829 for line in progress_note:
830 line[gmSOAPimporter.soap_bundle_CLIN_CTX_KEY] = clin_ctx
831
832 # dump progress note to backend
833 importer = gmSOAPimporter.cSOAPImporter()
834 if not importer.import_soap(progress_note):
835 gmGuiHelpers.gm_show_error(_('Error saving progress note.'), _('saving progress note'))
836 return False
837
838 # dump embedded data to backend
839 if not self.__embedded_data_holder.save():
840 gmGuiHelpers.gm_show_error (
841 _('Error saving embedded data.'),
842 _('saving progress note')
843 )
844 return False
845 self.__embedded_data_holder.clear()
846
847 return True
848 #--------------------------------------------------------
851 #--------------------------------------------------------
853 editor_content = self.GetValue()
854
855 for field_content in editor_content.values():
856 if field_content.text.strip() != u'':
857 return False
858
859 return True
860 #============================================================
862 """Basic progress note panel.
863
864 It provides a gmResizingWindow based progress note editor
865 with a header line. The header either displays the episode
866 this progress note is associated with or it allows for
867 entering an episode name. The episode name either names
868 an existing episode or is the name for a new episode.
869
870 This panel knows how to save it's data into the backend.
871
872 Can work as:
873 a) Progress note creation: displays an empty set of soap entries to
874 create a new soap note for the given episode (or unassociated)
875 """
876 #--------------------------------------------------------
878 """
879 Construct a new SOAP input widget.
880
881 @param parent: the parent widget
882
883 @param episode: the episode to create the SOAP editor for.
884 @type episode gmEMRStructItems.cEpisode instance or None (to create an
885 unassociated progress note). A gmEMRStructItems.cProblem instance is
886 also allowed to be passed, as the widget will obtain the related cEpisode.
887
888 @param input_defs: the display and associated data for each displayed narrative
889 @type input_defs: a list of cSOAPLineDef instances
890 """
891 if not isinstance(problem, (gmEMRStructItems.cHealthIssue, gmEMRStructItems.cEpisode, gmEMRStructItems.cProblem, types.NoneType)):
892 raise gmExceptions.ConstructorError, 'problem [%s] is of type %s, must be issue, episode, problem or None' % (str(problem), type(problem))
893
894 self.__is_saved = False
895 # do layout
896 wx.Panel.__init__(self, parent, -1, style = wx.NO_BORDER | wx.TAB_TRAVERSAL)
897 # - editor
898 if input_defs is None:
899 soap_lines = []
900 # make Richard the default ;-)
901 # FIXME: actually, should be read from backend
902 line = cSOAPLineDef()
903 line.label = _('Patient Request')
904 line.soap_cat = 's'
905 line.is_rfe = True
906 soap_lines.append(line)
907
908 line = cSOAPLineDef()
909 line.label = _('History Taken')
910 line.soap_cat = 's'
911 soap_lines.append(line)
912
913 line = cSOAPLineDef()
914 line.label = _('Findings')
915 line.soap_cat = 'o'
916 soap_lines.append(line)
917
918 line = cSOAPLineDef()
919 line.label = _('Assessment')
920 line.soap_cat = 'a'
921 soap_lines.append(line)
922
923 line = cSOAPLineDef()
924 line.label = _('Plan')
925 line.soap_cat = 'p'
926 soap_lines.append(line)
927 else:
928 soap_lines = input_defs
929 self.__soap_editor = cResizingSoapWin (
930 self,
931 size = wx.DefaultSize,
932 input_defs = soap_lines,
933 problem = problem
934 )
935 # - arrange
936 self.__szr_main = wx.BoxSizer(wx.VERTICAL)
937 self.__szr_main.Add(self.__soap_editor, 1, wx.EXPAND)
938 self.SetSizerAndFit(self.__szr_main)
939 #--------------------------------------------------------
940 # public API
941 #--------------------------------------------------------
943 """Retrieve the related problem for this SOAP input widget.
944 """
945 return self.__soap_editor.get_problem()
946 #--------------------------------------------------------
948 """
949 Retrieves whether the current editor is not associated
950 with any episode.
951 """
952 return ((self.__problem is None) or (self.__problem['type'] == 'issue'))
953 #--------------------------------------------------------
958 #--------------------------------------------------------
963 #--------------------------------------------------------
965 """
966 Set SOAP input widget saved (dumped to backend) state
967
968 @param is_saved: Flag indicating wether the SOAP has been dumped to
969 persistent backend
970 @type is_saved: boolean
971 """
972 self.__is_saved = is_saved
973 self.Clear()
974 #--------------------------------------------------------
976 """
977 Check SOAP input widget saved (dumped to backend) state
978 """
979 return self.__is_saved
980 #--------------------------------------------------------
982 return self.__soap_editor.save()
983 #--------------------------------------------------------
985 return self.__soap_editor.is_empty()
986 #============================================================
988 """if we separate it out like this it can transparently gain features"""
990 wx.TextCtrl.__init__(self, *args, **kwargs)
991 #============================================================
993 """Single Box free text SOAP input.
994
995 This widget was suggested by David Guest on the mailing
996 list. All it does is provide a single multi-line textbox
997 for typing free-text clinical notes which are stored as
998 Subjective.
999 """
1001 wx.Panel.__init__(self, *args, **kwargs)
1002 self.__do_layout()
1003 self.__pat = gmPerson.gmCurrentPatient()
1004 if not self.__register_events():
1005 raise gmExceptions.ConstructorError, 'cannot register interests'
1006 #--------------------------------------------------------
1008 # large box for free-text clinical notes
1009 self.__soap_box = cSingleBoxSOAP (
1010 self,
1011 -1,
1012 '',
1013 style = wx.TE_MULTILINE
1014 )
1015 # buttons below that
1016 self.__BTN_save = wx.Button(self, wx.NewId(), _("save"))
1017 self.__BTN_save.SetToolTipString(_('save clinical note in EMR'))
1018 self.__BTN_discard = wx.Button(self, wx.NewId(), _("discard"))
1019 self.__BTN_discard.SetToolTipString(_('discard clinical note'))
1020 szr_btns = wx.BoxSizer(wx.HORIZONTAL)
1021 szr_btns.Add(self.__BTN_save, 1, wx.ALIGN_CENTER_HORIZONTAL, 0)
1022 szr_btns.Add(self.__BTN_discard, 1, wx.ALIGN_CENTER_HORIZONTAL, 0)
1023 # arrange widgets
1024 szr_outer = wx.StaticBoxSizer(wx.StaticBox(self, -1, _("clinical progress note")), wx.VERTICAL)
1025 szr_outer.Add(self.__soap_box, 1, wx.EXPAND, 0)
1026 szr_outer.Add(szr_btns, 0, wx.EXPAND, 0)
1027 # and do layout
1028 self.SetAutoLayout(1)
1029 self.SetSizer(szr_outer)
1030 szr_outer.Fit(self)
1031 szr_outer.SetSizeHints(self)
1032 self.Layout()
1033 #--------------------------------------------------------
1035 # wxPython events
1036 wx.EVT_BUTTON(self.__BTN_save, self.__BTN_save.GetId(), self._on_save_note)
1037 wx.EVT_BUTTON(self.__BTN_discard, self.__BTN_discard.GetId(), self._on_discard_note)
1038
1039 # client internal signals
1040 gmDispatcher.connect(signal = 'pre_patient_selection', receiver = self._save_note)
1041 gmDispatcher.connect(signal = 'application_closing', receiver = self._save_note)
1042
1043 return True
1044 #--------------------------------------------------------
1045 # event handlers
1046 #--------------------------------------------------------
1049 #event.Skip()
1050 #--------------------------------------------------------
1054 #event.Skip()
1055 #--------------------------------------------------------
1056 # internal helpers
1057 #--------------------------------------------------------
1060 #--------------------------------------------------------
1062 # sanity checks
1063 if self.__pat is None:
1064 return True
1065 if not self.__pat.connected:
1066 return True
1067 if not self.__soap_box.IsModified():
1068 return True
1069 note = self.__soap_box.GetValue()
1070 if note.strip() == '':
1071 return True
1072 # now save note
1073 emr = self.__pat.get_emr()
1074 if emr is None:
1075 _log.error('cannot access clinical record of patient')
1076 return False
1077 if not emr.add_clin_narrative(note, soap_cat='s'):
1078 _log.error('error saving clinical note')
1079 return False
1080 self.__soap_box.SetValue('')
1081 return True
1082 #============================================================
1083 # main
1084 #------------------------------------------------------------
1085 if __name__ == "__main__":
1086
1087 import sys
1088
1089 from Gnumed.pycommon import gmPG2
1090 #--------------------------------------------------------
1092 """
1093 Retrieve the soap editor input lines definitions built from
1094 all the narratives for the given issue along a specific
1095 encounter.
1096
1097 @param pk_health_issue The id of the health issue to obtain the narratives for.
1098 @param pk_health_issue An integer instance
1099
1100 @param pk_encounter The id of the encounter to obtain the narratives for.
1101 @type A gmEMRStructItems.cEncounter instance.
1102
1103 @param default_labels: The user customized labels for each
1104 soap category.
1105 @type default_labels: A dictionary instance which keys are
1106 soap categories.
1107 """
1108 # custom labels
1109 if default_labels is None:
1110 default_labels = {
1111 's': _('History Taken'),
1112 'o': _('Findings'),
1113 'a': _('Assessment'),
1114 'p': _('Plan')
1115 }
1116
1117 pat = gmPerson.gmCurrentPatient()
1118 emr = pat.get_emr()
1119 soap_lines = []
1120 # for each soap cat
1121 for soap_cat in gmSOAPimporter.soap_bundle_SOAP_CATS:
1122 # retrieve narrative for given encounter
1123 narr_items = emr.get_clin_narrative (
1124 encounters = [pk_encounter],
1125 issues = [pk_health_issue],
1126 soap_cats = [soap_cat]
1127 )
1128 for narrative in narr_items:
1129 try:
1130 # FIXME: add more data such as doctor sig
1131 label_txt = default_labels[narrative['soap_cat']]
1132 except:
1133 label_txt = narrative['soap_cat']
1134 line = cSOAPLineDef()
1135 line.label = label_txt
1136 line.text = narrative['narrative']
1137 # line.data['narrative instance'] = narrative
1138 soap_lines.append(line)
1139 return soap_lines
1140 #--------------------------------------------------------
1142 print "test keyword must have been typed..."
1143 print "actually this would have to return a suitable wx.Window subclass instance"
1144 print "args:", args
1145 print "kwd args:"
1146 for key in kwargs.keys():
1147 print key, "->", kwargs[key]
1148 #--------------------------------------------------------
1150 msg = (
1151 "test keyword must have been typed...\n"
1152 "actually this would have to return a suitable wx.Window subclass instance\n"
1153 )
1154 for arg in args:
1155 msg = msg + "\narg ==> %s" % arg
1156 for key in kwargs.keys():
1157 msg = msg + "\n%s ==> %s" % (key, kwargs[key])
1158 gmGuiHelpers.gm_show_info (
1159 aMessage = msg,
1160 aTitle = 'msg box on create_widget from test_keyword'
1161 )
1162 #--------------------------------------------------------
1164 print 'testing notebooked soap input...'
1165 application = wx.PyWidgetTester(size=(800,500))
1166 soap_input = cProgressNoteInputNotebook(application.frame, -1)
1167 application.frame.Show(True)
1168 application.MainLoop()
1169 #--------------------------------------------------------
1171 print 'testing notebooked soap panel...'
1172 application = wx.PyWidgetTester(size=(800,500))
1173 soap_input = cNotebookedProgressNoteInputPanel(application.frame, -1)
1174 application.frame.Show(True)
1175 application.MainLoop()
1176 #--------------------------------------------------------
1177
1178 try:
1179 # obtain patient
1180 patient = gmPerson.ask_for_patient()
1181 if patient is None:
1182 print "No patient. Exiting gracefully..."
1183 sys.exit(0)
1184 gmPatSearchWidgets.set_active_patient(patient=patient)
1185
1186 #test_soap_notebook()
1187 test_soap_notebook_panel()
1188
1189 # # multisash soap
1190 # print 'testing multisashed soap input...'
1191 # application = wx.PyWidgetTester(size=(800,500))
1192 # soap_input = cMultiSashedProgressNoteInputPanel(application.frame, -1)
1193 # application.frame.Show(True)
1194 # application.MainLoop()
1195
1196 # # soap widget displaying all narratives for an issue along an encounter
1197 # print 'testing soap editor for encounter narratives...'
1198 # episode = gmEMRStructItems.cEpisode(aPK_obj=1)
1199 # encounter = gmEMRStructItems.cEncounter(aPK_obj=1)
1200 # narrative = get_narrative(pk_encounter = encounter['pk_encounter'], pk_health_issue = episode['pk_health_issue'])
1201 # default_labels = {'s':'Subjective', 'o':'Objective', 'a':'Assesment', 'p':'Plan'}
1202 # app = wx.PyWidgetTester(size=(300,500))
1203 # app.SetWidget(cResizingSoapPanel, episode, narrative)
1204 # app.MainLoop()
1205 # del app
1206
1207 # # soap progress note for episode
1208 # print 'testing soap editor for episode...'
1209 # app = wx.PyWidgetTester(size=(300,300))
1210 # app.SetWidget(cResizingSoapPanel, episode)
1211 # app.MainLoop()
1212 # del app
1213
1214 # # soap progress note for problem
1215 # print 'testing soap editor for problem...'
1216 # problem = gmEMRStructItems.cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': 1})
1217 # app = wx.PyWidgetTester(size=(300,300))
1218 # app.SetWidget(cResizingSoapPanel, problem)
1219 # app.MainLoop()
1220 # del app
1221
1222 # # unassociated soap progress note
1223 # print 'testing unassociated soap editor...'
1224 # app = wx.PyWidgetTester(size=(300,300))
1225 # app.SetWidget(cResizingSoapPanel, None)
1226 # app.MainLoop()
1227 # del app
1228
1229 # # unstructured progress note
1230 # print 'testing unstructured progress note...'
1231 # app = wx.PyWidgetTester(size=(600,600))
1232 # app.SetWidget(cSingleBoxSOAPPanel, -1)
1233 # app.MainLoop()
1234
1235 except StandardError:
1236 _log.exception("unhandled exception caught !")
1237 # but re-raise them
1238 raise
1239
1240 #============================================================
1241 # $Log: gmSOAPWidgets.py,v $
1242 # Revision 1.114 2010/01/11 19:59:13 ncq
1243 # - cleanup
1244 # - warn-on-unsaved-soap and use it
1245 #
1246 # Revision 1.113 2009/09/13 18:45:25 ncq
1247 # - no more get-active-encounter()
1248 #
1249 # Revision 1.112 2009/06/22 09:28:21 ncq
1250 # - improved wording as per list
1251 #
1252 # Revision 1.111 2009/06/20 22:39:27 ncq
1253 # - improved wording as per list discussion
1254 #
1255 # Revision 1.110 2009/06/04 16:30:30 ncq
1256 # - use set active patient from pat search widgets
1257 #
1258 # Revision 1.109 2009/02/10 18:40:07 ncq
1259 # - allow one editor per issue regardless of opening order
1260 #
1261 # Revision 1.108 2008/12/12 16:36:36 ncq
1262 # - better tooltip
1263 #
1264 # Revision 1.107 2008/11/20 20:17:50 ncq
1265 # - better docs
1266 #
1267 # Revision 1.106 2008/09/02 19:01:12 ncq
1268 # - adjust to clin health_issue fk_patient drop and related changes
1269 #
1270 # Revision 1.105 2008/08/08 13:32:59 ncq
1271 # - factor out save_unsaved_soap()
1272 # - register and act on pre-exit callback
1273 # - some cleanup
1274 #
1275 # Revision 1.104 2008/08/05 16:22:17 ncq
1276 # - cleanup
1277 #
1278 # Revision 1.103 2008/07/14 13:48:16 ncq
1279 # - properly save unsaved soap
1280 # - cleanup
1281 #
1282 # Revision 1.102 2008/07/10 21:04:10 ncq
1283 # - better comments
1284 # - experimental pre-selection SOAP saving
1285 #
1286 # Revision 1.101 2008/06/28 22:37:39 ncq
1287 # - visual improvement
1288 # - improved code comments
1289 # - add button to discard current editor
1290 # - prepare for saving soap on patient change/shutdown
1291 # - allow backing out of saving soap when asked for title of new episode
1292 #
1293 # Revision 1.100 2008/03/05 22:30:15 ncq
1294 # - new style logging
1295 #
1296 # Revision 1.99 2008/02/25 17:43:03 ncq
1297 # - keywords: ea -> ea$, phx -> phx$
1298 # - fix add_editor()
1299 # - clinical logic change for adding new editors
1300 #
1301 # Revision 1.98 2008/01/14 20:42:26 ncq
1302 # - don't crash on adding an empty editor
1303 #
1304 # Revision 1.97 2008/01/05 16:41:27 ncq
1305 # - remove logging from gm_show_*()
1306 #
1307 # Revision 1.96 2007/12/12 16:24:57 ncq
1308 # - explicit signals
1309 #
1310 # Revision 1.95 2007/09/24 22:05:57 ncq
1311 # - ask user for episode name when needed
1312 #
1313 # Revision 1.94 2007/08/12 00:12:41 ncq
1314 # - no more gmSignals.py
1315 #
1316 # Revision 1.93 2007/05/14 13:11:25 ncq
1317 # - use statustext() signal
1318 #
1319 # Revision 1.92 2007/03/08 11:53:59 ncq
1320 # - cleanup
1321 #
1322 # Revision 1.91 2007/03/02 15:39:13 ncq
1323 # - properly refresh widgets
1324 #
1325 # Revision 1.90 2007/02/17 14:02:11 ncq
1326 # - use improved coalesce()
1327 #
1328 # Revision 1.89 2007/02/04 16:14:23 ncq
1329 # - remove VSCROLL/HSCROLL for Mac
1330 #
1331 # Revision 1.88 2007/01/15 20:22:46 ncq
1332 # - move_episode_to_issue() is in gmEMRStructWidgets
1333 #
1334 # Revision 1.87 2007/01/15 13:05:38 ncq
1335 # - use move_episode_to_issue()
1336 #
1337 # Revision 1.86 2006/12/15 15:28:37 ncq
1338 # - signal problem saving progress note
1339 #
1340 # Revision 1.85 2006/11/28 20:53:41 ncq
1341 # - a missing Refresh()
1342 #
1343 # Revision 1.84 2006/11/26 17:13:17 ncq
1344 # - properly check for duplicate episode editors in add_editor when problem is None
1345 #
1346 # Revision 1.83 2006/11/24 10:01:31 ncq
1347 # - gm_beep_statustext() -> gm_statustext()
1348 #
1349 # Revision 1.82 2006/11/20 18:23:53 ncq
1350 # - smarten up add_editor() with allow_same_problem
1351 # - after save() open new unassociated editor if none there and refresh problem list
1352 #
1353 # Revision 1.81 2006/11/20 16:04:45 ncq
1354 # - improve problem list problem labels (show associated issue for episodes)
1355 #
1356 # Revision 1.80 2006/10/31 13:32:58 ncq
1357 # - cleanup
1358 # - require only rfe
1359 #
1360 # Revision 1.79 2006/10/25 07:46:44 ncq
1361 # - Format() -> strftime() since datetime.datetime does not have .Format()
1362 #
1363 # Revision 1.78 2006/10/25 07:25:38 ncq
1364 # - drop minimum soap entry as requested by user
1365 #
1366 # Revision 1.77 2006/10/23 13:23:04 ncq
1367 # - we don't need vacc widgets currently
1368 #
1369 # Revision 1.76 2006/10/08 11:08:42 ncq
1370 # - move to gmPG2 and only when testing
1371 #
1372 # Revision 1.75 2006/09/01 14:47:02 ncq
1373 # - fix typo
1374 #
1375 # Revision 1.74 2006/07/19 20:29:50 ncq
1376 # - import cleanup
1377 #
1378 # Revision 1.73 2006/06/20 14:26:36 ncq
1379 # - do not refresh problem list too early or threading will kill us
1380 #
1381 # Revision 1.72 2006/06/17 19:56:24 ncq
1382 # - immediately refresh problem list when episode_changed() signal arrives
1383 #
1384 # Revision 1.71 2006/06/17 14:26:30 ncq
1385 # - missing return True
1386 #
1387 # Revision 1.70 2006/06/17 14:00:03 ncq
1388 # - cleanup
1389 #
1390 # Revision 1.69 2006/05/15 13:36:00 ncq
1391 # - signal cleanup:
1392 # - activating_patient -> pre_patient_selection
1393 # - patient_selected -> post_patient_selection
1394 #
1395 # Revision 1.68 2006/05/12 12:18:11 ncq
1396 # - whoami -> whereami cleanup
1397 # - use gmCurrentProvider()
1398 #
1399 # Revision 1.67 2006/05/04 09:49:20 ncq
1400 # - get_clinical_record() -> get_emr()
1401 # - adjust to changes in set_active_patient()
1402 # - need explicit set_active_patient() after ask_for_patient() if wanted
1403 #
1404 # Revision 1.66 2006/01/03 12:12:03 ncq
1405 # - make epydoc happy re _()
1406 #
1407 # Revision 1.65 2005/12/27 19:01:07 ncq
1408 # - define vacc popup keyword just for testing
1409 # - slightly massage Syan's close-episodes-on-creation patch
1410 #
1411 # Revision 1.64 2005/12/27 02:52:40 sjtan
1412 #
1413 # 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.
1414 # Small logic error fixed where the id of the health_issue was passed in as the id of an episode.
1415 #
1416 # Revision 1.63 2005/12/26 12:03:10 sjtan
1417 #
1418 # more schema matching. some delegation .
1419 #
1420 # Revision 1.62 2005/10/21 09:25:52 ncq
1421 # - verify input structure in store_data()
1422 # - reorder __init__ so cSoapWin does not fail
1423 # - better recursion in data_sink setting for keywords
1424 #
1425 # Revision 1.61 2005/10/20 07:44:44 ncq
1426 # - cleanup++, some refactoring for clarity
1427 # - new way of handling popup data
1428 #
1429 # Revision 1.60 2005/10/04 13:09:49 sjtan
1430 # correct syntax errors; get soap entry working again.
1431 #
1432 # Revision 1.59 2005/09/28 21:27:30 ncq
1433 # - a lot of wx2.6-ification
1434 #
1435 # Revision 1.58 2005/09/28 15:57:48 ncq
1436 # - a whole bunch of wx.Foo -> wx.Foo
1437 #
1438 # Revision 1.57 2005/09/27 20:44:59 ncq
1439 # - wx.wx* -> wx.*
1440 #
1441 # Revision 1.56 2005/09/26 18:01:51 ncq
1442 # - use proper way to import wx26 vs wx2.4
1443 # - note: THIS WILL BREAK RUNNING THE CLIENT IN SOME PLACES
1444 # - time for fixup
1445 #
1446 # Revision 1.55 2005/09/26 04:31:27 ihaywood
1447 # allow problem to be passed to clinical popup EditAreas
1448 #
1449 # Revision 1.54 2005/09/12 15:10:43 ncq
1450 # - robustify auto-closing of episodes
1451 #
1452 # Revision 1.53 2005/09/11 17:39:54 ncq
1453 # - auto-close episodes older than 90 days when a new episode
1454 # for the same health issue is started by the user,
1455 # still lacking user interaction for "old" episodes younger than that
1456 #
1457 # Revision 1.52 2005/09/09 13:53:03 ncq
1458 # - make progress note editor deal with cProblem instances and
1459 # add appropriate casts in callers, thereby simplifying code
1460 # - auto-generate episode names where appropriate
1461 #
1462 # Revision 1.51 2005/08/22 13:27:47 ncq
1463 # - properly return error from SetHeadingTxt
1464 #
1465 # Revision 1.50 2005/07/21 21:01:26 ncq
1466 # - cleanup
1467 #
1468 # Revision 1.49 2005/06/20 13:15:02 cfmoro
1469 # Port to changes in cEpisodeSelector
1470 #
1471 # Revision 1.48 2005/05/17 08:10:44 ncq
1472 # - rearrange/relabel buttons/drop "discard" button on progress
1473 # notes notebook according to user feedback
1474 #
1475 # Revision 1.47 2005/05/14 14:59:41 ncq
1476 # - cleanups, teach proper levels to listen to signals
1477 # - listen to "activating_patient" so we can save progress notes *before* changing patient
1478 # - reset SOAP notebook on patient_selected
1479 #
1480 # Revision 1.46 2005/05/12 15:12:57 ncq
1481 # - improved problem list look and feel
1482 #
1483 # Revision 1.45 2005/05/08 21:49:11 ncq
1484 # - cleanup, improve test code
1485 # - add progress note editor notebook and use it
1486 # - teach cResizingSoapPanel how to save itself
1487 #
1488 # Revision 1.44 2005/05/06 15:32:11 ncq
1489 # - initial notebooked progress note input widget and test code
1490 #
1491 # Revision 1.43 2005/05/05 06:50:27 ncq
1492 # - more work on pre-0.1 issues: use BoxSizer instead of FlexGridSizer
1493 # for progress note editor so STC *should* occupy whole width of
1494 # multisash, however, redrawing makes it wrong again at times
1495 # - add dummy popup keywords for pending ICPC coding
1496 # - try to drop from heading to STC on enter
1497 # - make TAB move from heading to STC
1498 # - we might want to make the header part of the same TAB container as the STC
1499 #
1500 # Revision 1.42 2005/04/27 18:51:06 ncq
1501 # - slightly change Syans fix for the failing soap import to properly
1502 # take advantage of the existing infrastructure, my bad
1503 #
1504 # Revision 1.41 2005/04/27 14:49:38 sjtan
1505 #
1506 # allow the save clin_item to work by fixing a small bug where soap_cat isn't passed.
1507 #
1508 # Revision 1.40 2005/04/25 08:34:03 ncq
1509 # - cleanup
1510 # - don't display closed episodes in problem list
1511 # - don't wipe out half-baked progress notes when switching
1512 # back and forth after relevant backend changes
1513 #
1514 # Revision 1.39 2005/04/24 14:52:15 ncq
1515 # - use generic edit area popup for health issues
1516 #
1517 # Revision 1.38 2005/04/20 22:22:41 ncq
1518 # - create_vacc_popup/create_issue_popup
1519 #
1520 # Revision 1.37 2005/04/18 19:25:50 ncq
1521 # - configure Plan input field to popup vaccinations edit area
1522 # on keyword $vacc
1523 # - simplify cSoapLineDef because progress note input widget
1524 # is not used to *edit* progress notes ...
1525 #
1526 # Revision 1.36 2005/04/12 16:22:28 ncq
1527 # - remove faulty _()
1528 #
1529 # Revision 1.35 2005/04/12 10:06:06 ncq
1530 # - cleanup
1531 #
1532 # Revision 1.34 2005/04/03 20:18:27 ncq
1533 # - I feel haphazardous - enable actual progress note writing on [save] :-))
1534 #
1535 # Revision 1.33 2005/03/29 18:43:06 cfmoro
1536 # Removed debugging lines O:)
1537 #
1538 # Revision 1.32 2005/03/29 18:40:55 cfmoro
1539 # Fixed last encounter date when does not exists
1540 #
1541 # Revision 1.31 2005/03/29 07:31:01 ncq
1542 # - according to user feedback:
1543 # - switch sides for problem selection/progress note editor
1544 # - add header to problem list
1545 # - improve problem list formatting/display "last open"
1546 # - remove debugging code
1547 #
1548 # Revision 1.30 2005/03/18 16:48:41 cfmoro
1549 # Fixes to integrate multisash notes input plugin in wxclient
1550 #
1551 # Revision 1.29 2005/03/17 21:23:16 cfmoro
1552 # Using cClinicalRecord.problem2episode to take advantage of episode cache
1553 #
1554 # Revision 1.28 2005/03/17 19:53:13 cfmoro
1555 # Fixes derived from different combination of events. Replaced button state by per action sanity check for 0.1
1556 #
1557 # Revision 1.27 2005/03/17 17:48:20 cfmoro
1558 # Using types.NoneType to detect unassociated progress note
1559 #
1560 # Revision 1.26 2005/03/17 16:41:30 ncq
1561 # - properly allow explicit None episodes to indicate "unassociated"
1562 #
1563 # Revision 1.25 2005/03/17 13:35:23 ncq
1564 # - some cleanup
1565 #
1566 # Revision 1.24 2005/03/16 19:29:22 cfmoro
1567 # cResizingSoapPanel accepting cProblem instance of type episode
1568 #
1569 # Revision 1.23 2005/03/16 17:47:30 cfmoro
1570 # Minor fixes after moving the file. Restored test harness
1571 #
1572 # Revision 1.22 2005/03/15 08:07:52 ncq
1573 # - incorporated cMultiSashedProgressNoteInputPanel from Carlos' test area
1574 # - needs fixing/cleanup
1575 # - test harness needs to be ported
1576 #
1577 # Revision 1.21 2005/03/14 21:02:41 cfmoro
1578 # Handle changing text in unassociated notes
1579 #
1580 # Revision 1.20 2005/03/14 18:41:53 cfmoro
1581 # Indent fix
1582 #
1583 # Revision 1.19 2005/03/14 18:39:49 cfmoro
1584 # Clear phrasewheel on saving unassociated note
1585 #
1586 # Revision 1.18 2005/03/14 17:36:51 cfmoro
1587 # Added unit test for unassociated progress note
1588 #
1589 # Revision 1.17 2005/03/14 14:39:18 ncq
1590 # - somewhat improve Carlos' support for unassociated progress notes
1591 #
1592 # Revision 1.16 2005/03/13 09:05:06 cfmoro
1593 # Added intial support for unassociated progress notes
1594 #
1595 # Revision 1.15 2005/03/09 19:41:18 cfmoro
1596 # Decoupled cResizingSoapPanel from editing problem-encounter soap notes use case
1597 #
1598 # Revision 1.14 2005/03/04 19:44:28 cfmoro
1599 # Minor fixes from unit test
1600 #
1601 # Revision 1.13 2005/03/03 21:12:49 ncq
1602 # - some cleanups, switch to using data transfer classes
1603 # instead of complex and unwieldy dictionaries
1604 #
1605 # Revision 1.12 2005/02/23 03:20:44 cfmoro
1606 # Restores SetProblem function. Clean ups
1607 #
1608 # Revision 1.11 2005/02/21 19:07:42 ncq
1609 # - some cleanup
1610 #
1611 # Revision 1.10 2005/01/31 10:37:26 ncq
1612 # - gmPatient.py -> gmPerson.py
1613 #
1614 # Revision 1.9 2005/01/28 18:35:42 cfmoro
1615 # Removed problem idx number
1616 #
1617 # Revision 1.8 2005/01/18 13:38:24 ncq
1618 # - cleanup
1619 # - input_defs needs to be list as dict does not guarantee order
1620 # - make Richard-SOAP the default
1621 #
1622 # Revision 1.7 2005/01/17 19:55:28 cfmoro
1623 # Adapted to receive cProblem instances for SOAP edition
1624 #
1625 # Revision 1.6 2005/01/13 14:28:07 ncq
1626 # - cleanup
1627 #
1628 # Revision 1.5 2005/01/11 08:12:39 ncq
1629 # - fix a whole bunch of bugs from moving to main trunk
1630 #
1631 # Revision 1.4 2005/01/10 20:14:02 cfmoro
1632 # Import sys
1633 #
1634 # Revision 1.3 2005/01/10 17:50:36 ncq
1635 # - carry over last bits and pieces from test-area
1636 #
1637 # Revision 1.2 2005/01/10 17:48:03 ncq
1638 # - all of test_area/cfmoro/soap_input/gmSoapWidgets.py moved here
1639 #
1640 # Revision 1.1 2005/01/10 16:14:35 ncq
1641 # - soap widgets independant of the backend (gmPG) live in here
1642 #
1643 # Revision 1.13 2004/06/30 20:33:41 ncq
1644 # - add_clinical_note() -> add_clin_narrative()
1645 #
1646 # Revision 1.12 2004/03/09 07:54:32 ncq
1647 # - can call __save_note() from button press handler directly
1648 #
1649 # Revision 1.11 2004/03/08 23:35:10 shilbert
1650 # - adapt to new API from Gnumed.foo import bar
1651 #
1652 # Revision 1.10 2004/02/25 09:46:22 ncq
1653 # - import from pycommon now, not python-common
1654 #
1655 # Revision 1.9 2004/02/05 23:49:52 ncq
1656 # - use wxCallAfter()
1657 #
1658 # Revision 1.8 2003/11/09 14:29:11 ncq
1659 # - new API style in clinical record
1660 #
1661 # Revision 1.7 2003/10/26 01:36:13 ncq
1662 # - gmTmpPatient -> gmPatient
1663 #
1664 # Revision 1.6 2003/07/05 12:57:23 ncq
1665 # - catch one more error on saving note
1666 #
1667 # Revision 1.5 2003/06/26 22:26:04 ncq
1668 # - streamlined _save_note()
1669 #
1670 # Revision 1.4 2003/06/25 22:51:24 ncq
1671 # - now also handle signale application_closing()
1672 #
1673 # Revision 1.3 2003/06/24 12:57:05 ncq
1674 # - actually connect to backend
1675 # - save note on patient change and on explicit save request
1676 #
1677 # Revision 1.2 2003/06/22 16:20:33 ncq
1678 # - start backend connection
1679 #
1680 # Revision 1.1 2003/06/19 16:50:32 ncq
1681 # - let's make something simple but functional first
1682 #
1683 #
1684
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:41 2010 | http://epydoc.sourceforge.net |