Package Gnumed :: Package wxpython :: Module gmEncounterWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmEncounterWidgets

  1  """GNUmed encounter related widgets. 
  2   
  3  This module contains widgets to manage encounters.""" 
  4  #================================================================ 
  5  __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net" 
  6  __license__ = "GPL v2 or later" 
  7   
  8  # stdlib 
  9  import sys 
 10  import time 
 11  import logging 
 12  import datetime as pydt 
 13   
 14   
 15  # 3rd party 
 16  import wx 
 17   
 18   
 19  # GNUmed 
 20  if __name__ == '__main__': 
 21          sys.path.insert(0, '../../') 
 22  from Gnumed.pycommon import gmCfg 
 23  from Gnumed.pycommon import gmDateTime 
 24  from Gnumed.pycommon import gmTools 
 25  from Gnumed.pycommon import gmDispatcher 
 26  from Gnumed.pycommon import gmMatchProvider 
 27   
 28  from Gnumed.business import gmEMRStructItems 
 29  from Gnumed.business import gmPraxis 
 30  from Gnumed.business import gmPerson 
 31  from Gnumed.business import gmStaff 
 32   
 33  from Gnumed.wxpython import gmPhraseWheel 
 34  from Gnumed.wxpython import gmGuiHelpers 
 35  from Gnumed.wxpython import gmListWidgets 
 36  from Gnumed.wxpython import gmEditArea 
 37   
 38   
 39  _log = logging.getLogger('gm.ui') 
 40   
 41  #================================================================ 
 42  # encounter related widgets/functions 
 43  #---------------------------------------------------------------- 
44 -def _ask_for_encounter_continuation(new_encounter=None, fairly_recent_encounter=None):
45 """This is used as the callback when the EMR detects that the 46 patient was here rather recently and wants to ask the 47 provider whether to continue the recent encounter. 48 """ 49 # better safe than sorry 50 if new_encounter['pk_patient'] != fairly_recent_encounter['pk_patient']: 51 raise ValueError('pk_patient values on new (enc=%s pat=%s) and fairly-recent (enc=%s pat=%s) encounter do not match' % ( 52 new_encounter['pk_encounter'], 53 new_encounter['pk_patient'], 54 fairly_recent_encounter['pk_encounter'], 55 fairly_recent_encounter['pk_patient'] 56 )) 57 58 # only pester user if current patient is concerned 59 curr_pat = gmPerson.gmCurrentPatient() 60 if new_encounter['pk_patient'] != curr_pat.ID: 61 return 62 63 # ask user 64 msg = _( 65 '%s, %s [#%s]\n' 66 '\n' 67 "This patient's chart was worked on only recently:\n" 68 '\n' 69 ' %s' 70 '\n' 71 'Do you want to continue that consultation ?\n' 72 ' (If not a new one will be used.)\n' 73 ) % ( 74 curr_pat.get_description_gender(with_nickname = False), 75 gmDateTime.pydt_strftime(curr_pat['dob'], '%Y %b %d'), 76 curr_pat.ID, 77 fairly_recent_encounter.format ( 78 episodes = None, 79 with_soap = False, 80 left_margin = 1, 81 patient = None, 82 issues = None, 83 with_docs = False, 84 with_tests = False, 85 fancy_header = False, 86 with_vaccinations = False, 87 with_rfe_aoe = True, 88 with_family_history = False, 89 with_co_encountlet_hints = False, 90 by_episode = False 91 ) 92 ) 93 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 94 parent = None, 95 id = -1, 96 caption = _('Pulling chart'), 97 question = msg, 98 button_defs = [ 99 {'label': _('Continue recent'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False}, 100 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True} 101 ], 102 show_checkbox = False 103 ) 104 result = dlg.ShowModal() 105 dlg.DestroyLater() 106 107 # switch encounters 108 if result == wx.ID_YES: 109 _log.info('user wants to continue fairly-recent encounter') 110 curr_pat.emr.active_encounter = fairly_recent_encounter 111 if new_encounter.transfer_all_data_to_another_encounter(pk_target_encounter = fairly_recent_encounter['pk_encounter']): 112 if not gmEMRStructItems.delete_encounter(pk_encounter = new_encounter['pk_encounter']): 113 gmGuiHelpers.gm_show_info ( 114 _('Properly switched to fairly recent encounter but unable to delete newly-created encounter.'), 115 _('Pulling chart') 116 ) 117 else: 118 gmGuiHelpers.gm_show_info ( 119 _('Unable to transfer the data from newly-created to fairly recent encounter.'), 120 _('Pulling chart') 121 ) 122 return 123 124 _log.debug('stayed with newly created encounter')
125 126 #----------------------------------------------------------------
127 -def __ask_for_encounter_continuation(**kwargs):
128 try: 129 del kwargs['signal'] 130 del kwargs['sender'] 131 except KeyError: 132 pass 133 wx.CallAfter(_ask_for_encounter_continuation, **kwargs)
134 135 #---------------------------------------------------------------- 136 # listen for encounter continuation inquiry requests 137 gmDispatcher.connect(signal = 'ask_for_encounter_continuation', receiver = __ask_for_encounter_continuation) 138 139 #----------------------------------------------------------------
140 -def start_new_encounter(emr=None):
141 emr.start_new_encounter() 142 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True) 143 time.sleep(0.5) 144 gmGuiHelpers.gm_show_info ( 145 _('\nA new encounter was started for the active patient.\n'), 146 _('Start of new encounter') 147 )
148 149 #----------------------------------------------------------------
150 -def sanity_check_encounter_of_active_patient(parent=None, msg=None):
151 152 # FIXME: should consult a centralized security provider 153 # secretaries cannot edit encounters 154 if gmStaff.gmCurrentProvider()['role'] == 'secretary': 155 return True 156 157 pat = gmPerson.gmCurrentPatient() 158 if not pat.connected: 159 return True 160 161 dbcfg = gmCfg.cCfgSQL() 162 check_enc = bool(dbcfg.get2 ( 163 option = 'encounter.show_editor_before_patient_change', 164 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 165 bias = 'user', 166 default = True # True: if needed, not always unconditionally 167 )) 168 169 if not check_enc: 170 return True 171 172 emr = pat.emr 173 enc = emr.active_encounter 174 175 # did we add anything to the EMR ? 176 has_narr = enc.has_narrative() 177 has_docs = enc.has_documents() 178 179 if (not has_narr) and (not has_docs): 180 return True 181 182 empty_aoe = (gmTools.coalesce(enc['assessment_of_encounter'], '').strip() == '') 183 zero_duration = (enc['last_affirmed'] == enc['started']) 184 185 # all is well anyway 186 if (not empty_aoe) and (not zero_duration): 187 return True 188 189 if zero_duration: 190 enc['last_affirmed'] = pydt.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone) 191 192 # no narrative, presumably only import of docs and done 193 if not has_narr: 194 if empty_aoe: 195 enc['assessment_of_encounter'] = _('only documents added') 196 enc['pk_type'] = gmEMRStructItems.get_encounter_type(description = 'chart review')[0]['pk'] 197 # "last_affirmed" should be latest modified_at of relevant docs but that's a lot more involved 198 enc.save_payload() 199 return True 200 201 # does have narrative 202 if empty_aoe: 203 # - work out suitable default 204 epis = emr.get_episodes_by_encounter() 205 if len(epis) > 0: 206 enc_summary = '' 207 for epi in epis: 208 enc_summary += '%s; ' % epi['description'] 209 enc['assessment_of_encounter'] = enc_summary 210 211 if msg is None: 212 msg = _('Edit the encounter details of the active patient before moving on:') 213 if parent is None: 214 parent = wx.GetApp().GetTopWindow() 215 _log.debug('sanity-check editing encounter [%s] for patient [%s]', enc['pk_encounter'], enc['pk_patient']) 216 edit_encounter(parent = parent, encounter = enc, msg = msg) 217 218 return True
219 220 #----------------------------------------------------------------
221 -def edit_encounter(parent=None, encounter=None, msg=None, single_entry=False):
222 if parent is None: 223 parent = wx.GetApp().GetTopWindow() 224 225 # FIXME: use generic dialog 2 226 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter, msg = msg) 227 if dlg.ShowModal() == wx.ID_OK: 228 dlg.DestroyLater() 229 return True 230 dlg.DestroyLater() 231 return False
232 233 #----------------------------------------------------------------
234 -def manage_encounters(**kwargs):
235 return select_encounters(**kwargs)
236
237 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
238 239 if patient is None: 240 patient = gmPerson.gmCurrentPatient() 241 242 if not patient.connected: 243 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 244 return False 245 246 if parent is None: 247 parent = wx.GetApp().GetTopWindow() 248 249 emr = patient.emr 250 251 #-------------------- 252 def new(): 253 cfg_db = gmCfg.cCfgSQL() 254 enc_type = cfg_db.get2 ( 255 option = 'encounter.default_type', 256 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 257 bias = 'user' 258 ) 259 if enc_type is None: 260 enc_type = gmEMRStructItems.get_most_commonly_used_encounter_type() 261 if enc_type is None: 262 enc_type = 'in surgery' 263 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type) 264 saved = edit_encounter(parent = parent, encounter = enc) 265 if saved: 266 return True 267 gmEMRStructItems.delete_encounter(pk_encounter = enc['pk_encounter']) 268 return False
269 #-------------------- 270 def edit(enc=None): 271 if enc is None: 272 return False 273 return edit_encounter(parent = parent, encounter = enc) 274 #-------------------- 275 def edit_active(enc=None): 276 return edit_encounter(parent = parent, encounter = emr.active_encounter) 277 #-------------------- 278 def start_new(enc=None): 279 start_new_encounter(emr = emr) 280 return True 281 #-------------------- 282 def delete(enc=None): 283 if enc is None: 284 return False 285 question = _( 286 'Really delete encounter [%s] ?\n' 287 '\n' 288 'Once deletion succeeds it cannot be undone.\n' 289 '\n' 290 'Note that it will only succeed if there\n' 291 'is no data attached to the encounter.' 292 ) % enc['pk_encounter'] 293 delete_it = gmGuiHelpers.gm_show_question ( 294 question = question, 295 title = _('Deleting encounter'), 296 cancel_button = False 297 ) 298 if not delete_it: 299 return False 300 if gmEMRStructItems.delete_encounter(pk_encounter = enc['pk_encounter']): 301 return True 302 gmDispatcher.send ( 303 signal = 'statustext', 304 msg = _('Cannot delete encounter [%s]. It is probably in use.') % enc['pk_encounter'], 305 beep = True 306 ) 307 return False 308 #-------------------- 309 def get_tooltip(data): 310 if data is None: 311 return None 312 return data.format ( 313 patient = patient, 314 with_soap = False, 315 with_docs = False, 316 with_tests = False, 317 with_vaccinations = False, 318 with_rfe_aoe = True, 319 with_family_history = False, 320 by_episode=False, 321 fancy_header = True, 322 ) 323 #-------------------- 324 def refresh(lctrl): 325 if encounters is None: 326 encs = emr.get_encounters() 327 else: 328 encs = encounters 329 330 items = [ 331 [ 332 '%s - %s' % (gmDateTime.pydt_strftime(e['started'], '%Y %b %d %H:%M'), e['last_affirmed'].strftime('%H:%M')), 333 e['l10n_type'], 334 gmTools.coalesce(e['praxis_branch'], ''), 335 gmTools.coalesce(e['reason_for_encounter'], ''), 336 gmTools.coalesce(e['assessment_of_encounter'], ''), 337 gmTools.bool2subst(e.has_clinical_data(), '', gmTools.u_checkmark_thin), 338 e['pk_encounter'] 339 ] for e in encs 340 ] 341 lctrl.set_string_items(items = items) 342 lctrl.set_data(data = encs) 343 active_pk = emr.active_encounter['pk_encounter'] 344 for idx in range(len(encs)): 345 e = encs[idx] 346 if e['pk_encounter'] == active_pk: 347 lctrl.SetItemTextColour(idx, wx.Colour('RED')) 348 #-------------------- 349 return gmListWidgets.get_choices_from_list ( 350 parent = parent, 351 msg = _("The patient's encounters.\n"), 352 caption = _('Encounters ...'), 353 columns = [_('When'), _('Type'), _('Where'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'], 354 can_return_empty = False, 355 single_selection = single_selection, 356 refresh_callback = refresh, 357 edit_callback = edit, 358 new_callback = new, 359 delete_callback = delete, 360 list_tooltip_callback = get_tooltip, 361 ignore_OK_button = ignore_OK_button, 362 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active), 363 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new) 364 ) 365 366 #----------------------------------------------------------------
367 -class cEncounterPhraseWheel(gmPhraseWheel.cPhraseWheel):
368
369 - def __init__(self, *args, **kwargs):
370 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 371 372 cmd = """ 373 SELECT DISTINCT ON (list_label) 374 pk_encounter 375 AS data, 376 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type || ' [#' || pk_encounter || ']' 377 AS list_label, 378 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 379 AS field_label 380 FROM 381 clin.v_pat_encounters 382 WHERE 383 ( 384 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s 385 OR 386 l10n_type %(fragment_condition)s 387 OR 388 type %(fragment_condition)s 389 ) %(ctxt_patient)s 390 ORDER BY 391 list_label 392 LIMIT 393 30 394 """ 395 context = {'ctxt_patient': { 396 'where_part': 'AND pk_patient = %(patient)s', 397 'placeholder': 'patient' 398 }} 399 400 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context) 401 self.matcher._SQL_data2match = """ 402 SELECT 403 pk_encounter 404 AS data, 405 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type 406 AS list_label, 407 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 408 AS field_label 409 FROM 410 clin.v_pat_encounters 411 WHERE 412 pk_encounter = %(pk)s 413 """ 414 self.matcher.setThresholds(1, 3, 5) 415 #self.matcher.print_queries = True 416 self.selection_only = True 417 # outside code MUST bind this to a patient 418 self.set_context(context = 'patient', val = None)
419 #--------------------------------------------------------
420 - def set_from_instance(self, instance):
421 val = '%s: %s' % ( 422 gmDateTime.pydt_strftime(instance['started'], '%Y %b %d'), 423 instance['l10n_type'] 424 ) 425 self.SetText(value = val, data = instance['pk_encounter'])
426 #------------------------------------------------------------
427 - def _get_data_tooltip(self):
428 if self.GetData() is None: 429 return None 430 enc = gmEMRStructItems.cEncounter(aPK_obj = list(self._data.values())[0]['data']) 431 return enc.format ( 432 with_docs = False, 433 with_tests = False, 434 with_vaccinations = False, 435 with_family_history = False 436 )
437 438 #---------------------------------------------------------------- 439 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl 440
441 -class cEncounterEditAreaPnl(wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl):
442
443 - def __init__(self, *args, **kwargs):
444 try: 445 self.__encounter = kwargs['encounter'] 446 del kwargs['encounter'] 447 except KeyError: 448 self.__encounter = None 449 450 try: 451 msg = kwargs['msg'] 452 del kwargs['msg'] 453 except KeyError: 454 msg = None 455 456 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs) 457 458 self.refresh(msg = msg)
459 #-------------------------------------------------------- 460 # external API 461 #--------------------------------------------------------
462 - def refresh(self, encounter=None, msg=None):
463 464 if msg is not None: 465 self._LBL_instructions.SetLabel(msg) 466 467 if encounter is not None: 468 self.__encounter = encounter 469 470 if self.__encounter is None: 471 return True 472 473 # getting the patient via the encounter allows us to act 474 # on any encounter regardless of the currently active patient 475 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient']) 476 self._LBL_patient.SetLabel(pat.get_description_gender().strip()) 477 curr_pat = gmPerson.gmCurrentPatient() 478 if curr_pat.connected: 479 if curr_pat.ID == self.__encounter['pk_patient']: 480 self._LBL_patient.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 481 else: 482 self._LBL_patient.SetForegroundColour('red') 483 484 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data = self.__encounter['pk_type']) 485 self._PRW_location.Enable(True) 486 branch = self.__encounter.praxis_branch 487 if branch is None: # None or old entry because praxis has been re-configured 488 unit = self.__encounter.org_unit 489 if unit is None: # None 490 self._PRW_location.SetText('', data = None) 491 else: # old entry 492 self._PRW_location.Enable(False) 493 self._PRW_location.SetText(_('old praxis branch: %s (%s)') % (unit['unit'], unit['organization']), data = None) 494 else: 495 self._PRW_location.SetText(self.__encounter['praxis_branch'], data = branch['pk_praxis_branch']) 496 497 fts = gmDateTime.cFuzzyTimestamp ( 498 timestamp = self.__encounter['started'], 499 accuracy = gmDateTime.acc_minutes 500 ) 501 self._PRW_start.SetText(fts.format_accurately(), data=fts) 502 503 fts = gmDateTime.cFuzzyTimestamp ( 504 timestamp = self.__encounter['last_affirmed'], 505 accuracy = gmDateTime.acc_minutes 506 ) 507 self._PRW_end.SetText(fts.format_accurately(), data=fts) 508 509 # RFE 510 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], '')) 511 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe) 512 self._PRW_rfe_codes.SetText(val, data) 513 514 # AOE 515 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], '')) 516 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe) 517 self._PRW_aoe_codes.SetText(val, data) 518 519 # last affirmed 520 if self.__encounter['last_affirmed'] == self.__encounter['started']: 521 self._PRW_end.SetFocus() 522 else: 523 self._TCTRL_aoe.SetFocus() 524 525 return True
526 #--------------------------------------------------------
527 - def __is_valid_for_save(self):
528 529 if self._PRW_encounter_type.GetData() is None: 530 self._PRW_encounter_type.SetBackgroundColour('pink') 531 self._PRW_encounter_type.Refresh() 532 self._PRW_encounter_type.SetFocus() 533 return False 534 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) 535 self._PRW_encounter_type.Refresh() 536 537 # start 538 if self._PRW_start.GetValue().strip() == '': 539 self._PRW_start.SetBackgroundColour('pink') 540 self._PRW_start.Refresh() 541 self._PRW_start.SetFocus() 542 return False 543 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False): 544 self._PRW_start.SetBackgroundColour('pink') 545 self._PRW_start.Refresh() 546 self._PRW_start.SetFocus() 547 return False 548 self._PRW_start.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) 549 self._PRW_start.Refresh() 550 551 # last_affirmed 552 # if self._PRW_end.GetValue().strip() == u'': 553 # self._PRW_end.SetBackgroundColour('pink') 554 # self._PRW_end.Refresh() 555 # self._PRW_end.SetFocus() 556 # return False 557 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False): 558 self._PRW_end.SetBackgroundColour('pink') 559 self._PRW_end.Refresh() 560 self._PRW_end.SetFocus() 561 return False 562 self._PRW_end.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) 563 self._PRW_end.Refresh() 564 565 return True
566 #--------------------------------------------------------
567 - def save(self):
568 if not self.__is_valid_for_save(): 569 return False 570 571 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData() 572 self.__encounter['started'] = self._PRW_start.GetData().get_pydt() 573 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt() 574 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), '') 575 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), '') 576 self.__encounter.save_payload() # FIXME: error checking 577 578 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ] 579 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ] 580 581 return True
582 583 #---------------------------------------------------------------- 584 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg 585 586 # FIXME: use generic dialog 2
587 -class cEncounterEditAreaDlg(wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg):
588
589 - def __init__(self, *args, **kwargs):
590 encounter = kwargs['encounter'] 591 del kwargs['encounter'] 592 593 try: 594 button_defs = kwargs['button_defs'] 595 del kwargs['button_defs'] 596 except KeyError: 597 button_defs = None 598 599 try: 600 msg = kwargs['msg'] 601 del kwargs['msg'] 602 except KeyError: 603 msg = None 604 605 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs) 606 self.SetSize((450, 280)) 607 self.SetMinSize((450, 280)) 608 609 if button_defs is not None: 610 self._BTN_save.SetLabel(button_defs[0][0]) 611 self._BTN_save.SetToolTip(button_defs[0][1]) 612 self._BTN_close.SetLabel(button_defs[1][0]) 613 self._BTN_close.SetToolTip(button_defs[1][1]) 614 self.Refresh() 615 616 self._PNL_edit_area.refresh(encounter = encounter, msg = msg) 617 618 self.Fit()
619 #--------------------------------------------------------
620 - def _on_save_button_pressed(self, evt):
621 if self._PNL_edit_area.save(): 622 if self.IsModal(): 623 self.EndModal(wx.ID_OK) 624 else: 625 self.Close()
626 #--------------------------------------------------------
628 start = self._PRW_encounter_start.GetData() 629 if start is None: 630 return 631 start = start.get_pydt() 632 633 end = self._PRW_encounter_end.GetData() 634 if end is None: 635 fts = gmDateTime.cFuzzyTimestamp ( 636 timestamp = start, 637 accuracy = gmDateTime.acc_minutes 638 ) 639 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 640 return 641 end = end.get_pydt() 642 643 if start > end: 644 end = end.replace ( 645 year = start.year, 646 month = start.month, 647 day = start.day 648 ) 649 fts = gmDateTime.cFuzzyTimestamp ( 650 timestamp = end, 651 accuracy = gmDateTime.acc_minutes 652 ) 653 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 654 return 655 656 emr = self.__pat.emr 657 if start != emr.active_encounter['started']: 658 end = end.replace ( 659 year = start.year, 660 month = start.month, 661 day = start.day 662 ) 663 fts = gmDateTime.cFuzzyTimestamp ( 664 timestamp = end, 665 accuracy = gmDateTime.acc_minutes 666 ) 667 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 668 return 669 670 return
671 672 #---------------------------------------------------------------- 673 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl 674
675 -class cActiveEncounterPnl(wxgActiveEncounterPnl.wxgActiveEncounterPnl):
676
677 - def __init__(self, *args, **kwargs):
678 wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs) 679 self.__register_events() 680 self.refresh()
681 682 #------------------------------------------------------------
683 - def clear(self):
684 self._TCTRL_encounter.SetValue('') 685 self._TCTRL_encounter.SetToolTip('') 686 self._BTN_new.Enable(False) 687 self._BTN_list.Enable(False)
688 689 #------------------------------------------------------------
690 - def refresh(self):
691 self.clear() 692 693 pat = gmPerson.gmCurrentPatient() 694 if not pat.connected: 695 return 696 697 wx.CallAfter(self.__refresh)
698 699 #------------------------------------------------------------
700 - def __refresh(self):
701 enc = gmPerson.gmCurrentPatient().emr.active_encounter 702 self._TCTRL_encounter.SetValue(enc.format ( 703 with_docs = False, 704 with_tests = False, 705 fancy_header = False, 706 with_vaccinations = False, 707 with_family_history = False).strip('\n') 708 ) 709 self._TCTRL_encounter.SetToolTip ( 710 _('The active encounter of the current patient:\n\n%s') % enc.format( 711 with_docs = False, 712 with_tests = False, 713 fancy_header = True, 714 with_vaccinations = False, 715 with_rfe_aoe = True, 716 with_family_history = False).strip('\n') 717 ) 718 self._BTN_new.Enable(True) 719 self._BTN_list.Enable(True)
720 721 #------------------------------------------------------------
722 - def __register_events(self):
723 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick) 724 725 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 726 # this would throw an exception due to concurrency issues: 727 #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self.refresh) 728 gmDispatcher.connect(signal = 'clin.episode_mod_db', receiver = self.refresh) 729 gmDispatcher.connect(signal = 'current_encounter_modified', receiver = self.refresh) 730 gmDispatcher.connect(signal = 'current_encounter_switched', receiver = self.refresh)
731 732 #------------------------------------------------------------ 733 # event handler 734 #------------------------------------------------------------
736 self.clear()
737 738 #------------------------------------------------------------
739 - def _on_ldclick(self, event):
740 pat = gmPerson.gmCurrentPatient() 741 if not pat.connected: 742 return 743 edit_encounter(encounter = pat.emr.active_encounter)
744 745 #------------------------------------------------------------
746 - def _on_new_button_pressed(self, event):
747 pat = gmPerson.gmCurrentPatient() 748 if not pat.connected: 749 return 750 start_new_encounter(emr = pat.emr)
751 752 #------------------------------------------------------------
753 - def _on_list_button_pressed(self, event):
754 if not gmPerson.gmCurrentPatient().connected: 755 return 756 select_encounters()
757 758 #================================================================ 759 # encounter TYPE related widgets 760 #----------------------------------------------------------------
761 -def edit_encounter_type(parent=None, encounter_type=None):
762 ea = cEncounterTypeEditAreaPnl(parent, -1) 763 ea.data = encounter_type 764 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit') 765 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea) 766 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name'))) 767 if dlg.ShowModal() == wx.ID_OK: 768 return True 769 return False
770 771 #----------------------------------------------------------------
772 -def manage_encounter_types(parent=None):
773 774 if parent is None: 775 parent = wx.GetApp().GetTopWindow() 776 777 #-------------------- 778 def edit(enc_type=None): 779 return edit_encounter_type(parent = parent, encounter_type = enc_type)
780 #-------------------- 781 def delete(enc_type=None): 782 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']): 783 return True 784 gmDispatcher.send ( 785 signal = 'statustext', 786 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'], 787 beep = True 788 ) 789 return False 790 #-------------------- 791 def refresh(lctrl): 792 enc_types = gmEMRStructItems.get_encounter_types() 793 lctrl.set_string_items(items = enc_types) 794 #-------------------- 795 gmListWidgets.get_choices_from_list ( 796 parent = parent, 797 msg = _('\nSelect the encounter type you want to edit !\n'), 798 caption = _('Managing encounter types ...'), 799 columns = [_('Local name'), _('Encounter type')], 800 single_selection = True, 801 edit_callback = edit, 802 new_callback = edit, 803 delete_callback = delete, 804 refresh_callback = refresh 805 ) 806 807 #---------------------------------------------------------------- 808 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl 809
810 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
811
812 - def __init__(self, *args, **kwargs):
816 817 # self.__register_interests() 818 #------------------------------------------------------- 819 # generic edit area API 820 #-------------------------------------------------------
821 - def _valid_for_save(self):
822 if self.mode == 'edit': 823 if self._TCTRL_l10n_name.GetValue().strip() == '': 824 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 825 return False 826 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 827 return True 828 829 no_errors = True 830 831 if self._TCTRL_l10n_name.GetValue().strip() == '': 832 if self._TCTRL_name.GetValue().strip() == '': 833 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 834 no_errors = False 835 else: 836 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 837 else: 838 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 839 840 if self._TCTRL_name.GetValue().strip() == '': 841 if self._TCTRL_l10n_name.GetValue().strip() == '': 842 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False) 843 no_errors = False 844 else: 845 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 846 else: 847 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 848 849 return no_errors
850 #-------------------------------------------------------
851 - def _save_as_new(self):
852 enc_type = gmEMRStructItems.create_encounter_type ( 853 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), ''), 854 l10n_description = gmTools.coalesce ( 855 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), ''), 856 self._TCTRL_name.GetValue().strip() 857 ) 858 ) 859 if enc_type is None: 860 return False 861 self.data = enc_type 862 return True
863 #-------------------------------------------------------
864 - def _save_as_update(self):
865 enc_type = gmEMRStructItems.update_encounter_type ( 866 description = self._TCTRL_name.GetValue().strip(), 867 l10n_description = self._TCTRL_l10n_name.GetValue().strip() 868 ) 869 if enc_type is None: 870 return False 871 self.data = enc_type 872 return True
873 #-------------------------------------------------------
874 - def _refresh_as_new(self):
875 self._TCTRL_l10n_name.SetValue('') 876 self._TCTRL_name.SetValue('') 877 self._TCTRL_name.Enable(True)
878 #-------------------------------------------------------
879 - def _refresh_from_existing(self):
880 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 881 self._TCTRL_name.SetValue(self.data['description']) 882 # disallow changing type on all encounters by editing system name 883 self._TCTRL_name.Enable(False)
884 #-------------------------------------------------------
886 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 887 self._TCTRL_name.SetValue(self.data['description']) 888 self._TCTRL_name.Enable(True)
889 #------------------------------------------------------- 890 # internal API 891 #------------------------------------------------------- 892 # def __register_interests(self): 893 # return 894 895 #----------------------------------------------------------------
896 -class cEncounterTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
897 """Phrasewheel to allow selection of encounter type. 898 899 - user input interpreted as encounter type in English or local language 900 - data returned is pk of corresponding encounter type or None 901 """
902 - def __init__(self, *args, **kwargs):
903 904 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 905 906 mp = gmMatchProvider.cMatchProvider_SQL2 ( 907 queries = [ 908 """ 909 SELECT 910 data, 911 field_label, 912 list_label 913 FROM ( 914 SELECT DISTINCT ON (data) * 915 FROM ( 916 SELECT 917 pk AS data, 918 _(description) AS field_label, 919 case 920 when _(description) = description then _(description) 921 else _(description) || ' (' || description || ')' 922 end AS list_label 923 FROM 924 clin.encounter_type 925 WHERE 926 _(description) %(fragment_condition)s 927 OR 928 description %(fragment_condition)s 929 ) AS q_distinct_pk 930 ) AS q_ordered 931 ORDER BY 932 list_label 933 """ ] 934 ) 935 mp.setThresholds(2, 4, 6) 936 937 self.matcher = mp 938 self.selection_only = True 939 self.picklist_delay = 50
940 941 #================================================================ 942 # main 943 #---------------------------------------------------------------- 944 if __name__ == '__main__': 945 946 if len(sys.argv) < 2: 947 sys.exit() 948 949 if sys.argv[1] != 'test': 950 sys.exit() 951 952 from Gnumed.pycommon import gmI18N 953 gmI18N.activate_locale() 954 gmI18N.install_domain() 955 956 #----------------------------------------------------------------
957 - def test_encounter_edit_area_panel():
958 app = wx.PyWidgetTester(size = (200, 300)) 959 emr = pat.emr 960 enc = emr.active_encounter 961 #enc = gmEMRStructItems.cEncounter(1) 962 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc) 963 app.frame.Show(True) 964 app.MainLoop() 965 return
966 #----------------------------------------------------------------
967 - def test_encounter_edit_area_dialog():
968 app = wx.PyWidgetTester(size = (200, 300)) 969 emr = pat.emr 970 enc = emr.active_encounter 971 #enc = gmEMRStructItems.cEncounter(1) 972 973 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc) 974 dlg.ShowModal()
975 976 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc) 977 # app.frame.Show(True) 978 # app.MainLoop() 979 980 #---------------------------------------------------------------- 981 #test_encounter_edit_area_panel() 982 #test_encounter_edit_area_dialog() 983