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 popup47 #============================================================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 popup68 #============================================================ 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 """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 #============================================================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 result220 #--------------------------------------------------------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 True256 #--------------------------------------------------------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 True308 #-------------------------------------------------------- 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()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 #--------------------------------------------------------626 #============================================================ 634 #============================================================ 635 # FIXME: this should be a more generic(ally named) class 636 # FIXME: living elsewhere374 """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 True516 #-------------------------------------------------------- 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 False607 #--------------------------------------------------------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 True621 #-------------------------------------------------------- 622 # notebook plugin API 623 #--------------------------------------------------------638 639 _data_savers = {} 640 643 #--------------------------------------------------------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 #============================================================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 True666 #--------------------------------------------------------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 True678 #--------------------------------------------------------690860 #============================================================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 = None756 #-------------------------------------------------------- 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 True848 #-------------------------------------------------------- 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 True862 """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 #--------------------------------------------------------986 #============================================================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_saved980 #--------------------------------------------------------982 return self.__soap_editor.save()983 #--------------------------------------------------------985 return self.__soap_editor.is_empty()988 """if we separate it out like this it can transparently gain features"""991 #============================================================990 wx.TextCtrl.__init__(self, *args, **kwargs)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 """1082 #============================================================ 1083 # main 1084 #------------------------------------------------------------ 1085 if __name__ == "__main__": 1086 1087 import sys 1088 1089 from Gnumed.pycommon import gmPG2 1090 #--------------------------------------------------------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 True1044 #-------------------------------------------------------- 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 True1092 """ 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_lines1140 #--------------------------------------------------------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 |