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

Source Code for Module Gnumed.wxpython.gmEMRStructWidgets

   1  """GNUmed EMR structure editors 
   2   
   3          This module contains widgets to create and edit EMR structural 
   4          elements (issues, enconters, episodes). 
   5   
   6          This is based on initial work and ideas by Syan <kittylitter@swiftdsl.com.au> 
   7          and Karsten <Karsten.Hilbert@gmx.net>. 
   8  """ 
   9  #================================================================ 
  10  __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net" 
  11  __license__ = "GPL v2 or later" 
  12   
  13  # stdlib 
  14  import sys 
  15  import time 
  16  import logging 
  17  import datetime as pydt 
  18   
  19   
  20  # 3rd party 
  21  import wx 
  22   
  23   
  24  # GNUmed 
  25  if __name__ == '__main__': 
  26          sys.path.insert(0, '../../') 
  27  from Gnumed.pycommon import gmI18N 
  28  from Gnumed.pycommon import gmExceptions 
  29  from Gnumed.pycommon import gmCfg 
  30  from Gnumed.pycommon import gmDateTime 
  31  from Gnumed.pycommon import gmTools 
  32  from Gnumed.pycommon import gmDispatcher 
  33  from Gnumed.pycommon import gmMatchProvider 
  34   
  35  from Gnumed.business import gmEMRStructItems 
  36  from Gnumed.business import gmPraxis 
  37  from Gnumed.business import gmPerson 
  38   
  39  from Gnumed.wxpython import gmPhraseWheel 
  40  from Gnumed.wxpython import gmGuiHelpers 
  41  from Gnumed.wxpython import gmListWidgets 
  42  from Gnumed.wxpython import gmEditArea 
  43   
  44   
  45  _log = logging.getLogger('gm.ui') 
  46  #================================================================ 
  47  # EMR access helper functions 
  48  #---------------------------------------------------------------- 
49 -def emr_access_spinner(time2spin=0):
50 """Spin time in seconds.""" 51 if time2spin == 0: 52 return 53 sleep_time = 0.1 54 total_rounds = int(time2spin / sleep_time) 55 if total_rounds < 1: 56 return 57 rounds = 0 58 while rounds < total_rounds: 59 wx.Yield() 60 time.sleep(sleep_time) 61 rounds += 1
62 #================================================================ 63 # performed procedure related widgets/functions 64 #----------------------------------------------------------------
65 -def manage_performed_procedures(parent=None):
66 67 pat = gmPerson.gmCurrentPatient() 68 emr = pat.get_emr() 69 70 if parent is None: 71 parent = wx.GetApp().GetTopWindow() 72 #----------------------------------------- 73 def edit(procedure=None): 74 return edit_procedure(parent = parent, procedure = procedure)
75 #----------------------------------------- 76 def delete(procedure=None): 77 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']): 78 return True 79 80 gmDispatcher.send ( 81 signal = u'statustext', 82 msg = _('Cannot delete performed procedure.'), 83 beep = True 84 ) 85 return False 86 #----------------------------------------- 87 def refresh(lctrl): 88 procs = emr.get_performed_procedures() 89 90 items = [ 91 [ 92 u'%s%s' % ( 93 p['clin_when'].strftime('%Y-%m-%d'), 94 gmTools.bool2subst ( 95 p['is_ongoing'], 96 _(' (ongoing)'), 97 gmTools.coalesce ( 98 initial = p['clin_end'], 99 instead = u'', 100 template_initial = u' - %s', 101 function_initial = ('strftime', u'%Y-%m-%d') 102 ) 103 ) 104 ), 105 p['clin_where'], 106 p['episode'], 107 p['performed_procedure'] 108 ] for p in procs 109 ] 110 lctrl.set_string_items(items = items) 111 lctrl.set_data(data = procs) 112 #----------------------------------------- 113 gmListWidgets.get_choices_from_list ( 114 parent = parent, 115 msg = _('\nSelect the procedure you want to edit !\n'), 116 caption = _('Editing performed procedures ...'), 117 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')], 118 single_selection = True, 119 edit_callback = edit, 120 new_callback = edit, 121 delete_callback = delete, 122 refresh_callback = refresh 123 ) 124 #----------------------------------------------------------------
125 -def edit_procedure(parent=None, procedure=None):
126 ea = cProcedureEAPnl(parent = parent, id = -1) 127 ea.data = procedure 128 ea.mode = gmTools.coalesce(procedure, 'new', 'edit') 129 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 130 dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure'))) 131 if dlg.ShowModal() == wx.ID_OK: 132 dlg.Destroy() 133 return True 134 dlg.Destroy() 135 return False
136 #---------------------------------------------------------------- 137 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl 138
139 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
140
141 - def __init__(self, *args, **kwargs):
142 wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs) 143 gmEditArea.cGenericEditAreaMixin.__init__(self) 144 145 self.mode = 'new' 146 self.data = None 147 148 self.__init_ui()
149 #----------------------------------------------------------------
150 - def __init_ui(self):
151 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus) 152 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID) 153 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus) 154 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus) 155 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus) 156 157 # location 158 mp = gmMatchProvider.cMatchProvider_SQL2 ( 159 queries = [ 160 u""" 161 SELECT DISTINCT ON (data) data, location 162 FROM ( 163 SELECT 164 clin_where as data, 165 clin_where as location 166 FROM 167 clin.procedure 168 WHERE 169 clin_where %(fragment_condition)s 170 171 UNION ALL 172 173 SELECT 174 narrative as data, 175 narrative as location 176 FROM 177 clin.hospital_stay 178 WHERE 179 narrative %(fragment_condition)s 180 ) as union_result 181 ORDER BY data 182 LIMIT 25""" 183 ] 184 ) 185 mp.setThresholds(2, 4, 6) 186 self._PRW_location.matcher = mp 187 188 # procedure 189 mp = gmMatchProvider.cMatchProvider_SQL2 ( 190 queries = [ 191 u""" 192 select distinct on (narrative) narrative, narrative 193 from clin.procedure 194 where narrative %(fragment_condition)s 195 order by narrative 196 limit 25 197 """ ] 198 ) 199 mp.setThresholds(2, 4, 6) 200 self._PRW_procedure.matcher = mp
201 #----------------------------------------------------------------
203 stay = self._PRW_hospital_stay.GetData() 204 if stay is None: 205 self._PRW_hospital_stay.SetText() 206 self._PRW_location.Enable(True) 207 self._PRW_episode.Enable(True) 208 self._LBL_hospital_details.SetLabel(u'') 209 else: 210 self._PRW_location.SetText() 211 self._PRW_location.Enable(False) 212 self._PRW_episode.SetText() 213 self._PRW_episode.Enable(False) 214 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
215 #----------------------------------------------------------------
216 - def _on_location_lost_focus(self):
217 if self._PRW_location.GetValue().strip() == u'': 218 self._PRW_hospital_stay.Enable(True) 219 # self._PRW_episode.Enable(False) 220 else: 221 self._PRW_hospital_stay.SetText() 222 self._PRW_hospital_stay.Enable(False) 223 self._PRW_hospital_stay.display_as_valid(True)
224 # self._PRW_episode.Enable(True) 225 #----------------------------------------------------------------
226 - def _on_start_lost_focus(self):
227 if not self._DPRW_date.is_valid_timestamp(): 228 return 229 end = self._DPRW_end.GetData() 230 if end is None: 231 return 232 end = end.get_pydt() 233 start = self._DPRW_date.GetData().get_pydt() 234 if start < end: 235 return 236 self._DPRW_date.display_as_valid(False)
237 #----------------------------------------------------------------
238 - def _on_end_lost_focus(self):
239 end = self._DPRW_end.GetData() 240 if end is None: 241 self._CHBOX_ongoing.Enable(True) 242 self._DPRW_end.display_as_valid(True) 243 else: 244 self._CHBOX_ongoing.Enable(False) 245 end = end.get_pydt() 246 now = gmDateTime.pydt_now_here() 247 if end > now: 248 self._CHBOX_ongoing.SetValue(True) 249 else: 250 self._CHBOX_ongoing.SetValue(False) 251 start = self._DPRW_date.GetData() 252 if start is None: 253 self._DPRW_end.display_as_valid(True) 254 else: 255 start = start.get_pydt() 256 if end > start: 257 self._DPRW_end.display_as_valid(True) 258 else: 259 self._DPRW_end.display_as_valid(False)
260 #---------------------------------------------------------------- 261 # generic Edit Area mixin API 262 #----------------------------------------------------------------
263 - def _valid_for_save(self):
264 265 has_errors = False 266 267 if not self._DPRW_date.is_valid_timestamp(): 268 self._DPRW_date.display_as_valid(False) 269 has_errors = True 270 else: 271 self._DPRW_date.display_as_valid(True) 272 273 start = self._DPRW_date.GetData() 274 end = self._DPRW_end.GetData() 275 self._DPRW_end.display_as_valid(True) 276 if end is not None: 277 end = end.get_pydt() 278 if start is not None: 279 start = start.get_pydt() 280 if end < start: 281 has_errors = True 282 self._DPRW_end.display_as_valid(False) 283 if self._CHBOX_ongoing.IsChecked(): 284 now = gmDateTime.pydt_now_here() 285 if end < now: 286 has_errors = True 287 self._DPRW_end.display_as_valid(False) 288 289 if self._PRW_hospital_stay.GetData() is None: 290 if self._PRW_episode.GetData() is None: 291 self._PRW_episode.display_as_valid(False) 292 has_errors = True 293 else: 294 self._PRW_episode.display_as_valid(True) 295 else: 296 self._PRW_episode.display_as_valid(True) 297 298 if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''): 299 self._PRW_procedure.display_as_valid(False) 300 has_errors = True 301 else: 302 self._PRW_procedure.display_as_valid(True) 303 304 invalid_location = ( 305 (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'') 306 or 307 (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'') 308 ) 309 if invalid_location: 310 self._PRW_hospital_stay.display_as_valid(False) 311 self._PRW_location.display_as_valid(False) 312 has_errors = True 313 else: 314 self._PRW_hospital_stay.display_as_valid(True) 315 self._PRW_location.display_as_valid(True) 316 317 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save procedure.'), beep = True) 318 319 return (has_errors is False)
320 #----------------------------------------------------------------
321 - def _save_as_new(self):
322 323 pat = gmPerson.gmCurrentPatient() 324 emr = pat.get_emr() 325 326 if self._PRW_hospital_stay.GetData() is None: 327 stay = None 328 epi = self._PRW_episode.GetData() 329 loc = self._PRW_location.GetValue().strip() 330 else: 331 stay = self._PRW_hospital_stay.GetData() 332 epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode'] 333 loc = None 334 335 proc = emr.add_performed_procedure ( 336 episode = epi, 337 location = loc, 338 hospital_stay = stay, 339 procedure = self._PRW_procedure.GetValue().strip() 340 ) 341 342 proc['clin_when'] = self._DPRW_date.GetData().get_pydt() 343 if self._DPRW_end.GetData() is None: 344 proc['clin_end'] = None 345 else: 346 proc['clin_end'] = self._DPRW_end.GetData().get_pydt() 347 proc['is_ongoing'] = self._CHBOX_ongoing.IsChecked() 348 proc.save() 349 350 proc.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 351 352 self.data = proc 353 354 return True
355 #----------------------------------------------------------------
356 - def _save_as_update(self):
357 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt() 358 359 if self._DPRW_end.GetData() is None: 360 self.data['clin_end'] = None 361 else: 362 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt() 363 364 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked() 365 366 if self._PRW_hospital_stay.GetData() is None: 367 self.data['pk_hospital_stay'] = None 368 self.data['clin_where'] = self._PRW_location.GetValue().strip() 369 self.data['pk_episode'] = self._PRW_episode.GetData() 370 else: 371 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData() 372 self.data['clin_where'] = None 373 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData()) 374 self.data['pk_episode'] = stay['pk_episode'] 375 376 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip() 377 378 self.data.save() 379 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 380 381 return True
382 #----------------------------------------------------------------
383 - def _refresh_as_new(self):
384 self._DPRW_date.SetText() 385 self._DPRW_end.SetText() 386 self._CHBOX_ongoing.SetValue(False) 387 self._CHBOX_ongoing.Enable(True) 388 self._PRW_hospital_stay.SetText() 389 self._PRW_location.SetText() 390 self._PRW_episode.SetText() 391 self._PRW_procedure.SetText() 392 self._PRW_codes.SetText() 393 394 self._PRW_procedure.SetFocus()
395 #----------------------------------------------------------------
396 - def _refresh_from_existing(self):
397 self._DPRW_date.SetData(data = self.data['clin_when']) 398 if self.data['clin_end'] is None: 399 self._DPRW_end.SetText() 400 self._CHBOX_ongoing.Enable(True) 401 self._CHBOX_ongoing.SetValue(self.data['is_ongoing']) 402 else: 403 self._DPRW_end.SetData(data = self.data['clin_end']) 404 self._CHBOX_ongoing.Enable(False) 405 now = gmDateTime.pydt_now_here() 406 if self.data['clin_end'] > now: 407 self._CHBOX_ongoing.SetValue(True) 408 else: 409 self._CHBOX_ongoing.SetValue(False) 410 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode']) 411 self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure']) 412 413 if self.data['pk_hospital_stay'] is None: 414 self._PRW_hospital_stay.SetText() 415 self._LBL_hospital_details.SetLabel(u'') 416 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where']) 417 else: 418 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay']) 419 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = self.data['pk_hospital_stay']).format()) 420 self._PRW_location.SetText() 421 422 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 423 self._PRW_codes.SetText(val, data) 424 425 self._PRW_procedure.SetFocus()
426 #----------------------------------------------------------------
428 self._refresh_as_new() 429 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode']) 430 if self.data['pk_hospital_stay'] is None: 431 self._PRW_hospital_stay.SetText() 432 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where']) 433 else: 434 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay']) 435 self._PRW_location.SetText() 436 437 self._PRW_procedure.SetFocus()
438 #---------------------------------------------------------------- 439 # event handlers 440 #----------------------------------------------------------------
442 # FIXME: this would benefit from setting the created stay 443 edit_hospital_stay(parent = self.GetParent()) 444 evt.Skip()
445 #----------------------------------------------------------------
446 - def _on_ongoing_checkbox_checked(self, event):
447 if self._CHBOX_ongoing.IsChecked(): 448 end = self._DPRW_end.GetData() 449 if end is None: 450 self._DPRW_end.display_as_valid(True) 451 else: 452 end = end.get_pydt() 453 now = gmDateTime.pydt_now_here() 454 if end > now: 455 self._DPRW_end.display_as_valid(True) 456 else: 457 self._DPRW_end.display_as_valid(False) 458 else: 459 self._DPRW_end.is_valid_timestamp() 460 event.Skip()
461 #================================================================ 462 # hospitalizations related widgets/functions 463 #----------------------------------------------------------------
464 -def manage_hospital_stays(parent=None):
465 466 pat = gmPerson.gmCurrentPatient() 467 emr = pat.get_emr() 468 469 if parent is None: 470 parent = wx.GetApp().GetTopWindow() 471 #----------------------------------------- 472 def get_tooltip(stay=None): 473 if stay is None: 474 return None 475 return stay.format ( 476 include_procedures = True, 477 include_docs = True 478 )
479 #----------------------------------------- 480 def edit(stay=None): 481 return edit_hospital_stay(parent = parent, hospital_stay = stay) 482 #----------------------------------------- 483 def delete(stay=None): 484 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']): 485 return True 486 gmDispatcher.send ( 487 signal = u'statustext', 488 msg = _('Cannot delete hospitalization.'), 489 beep = True 490 ) 491 return False 492 #----------------------------------------- 493 def refresh(lctrl): 494 stays = emr.get_hospital_stays() 495 items = [ 496 [ 497 s['admission'].strftime('%Y-%m-%d'), 498 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')), 499 s['episode'], 500 gmTools.coalesce(s['hospital'], u'') 501 ] for s in stays 502 ] 503 lctrl.set_string_items(items = items) 504 lctrl.set_data(data = stays) 505 #----------------------------------------- 506 gmListWidgets.get_choices_from_list ( 507 parent = parent, 508 msg = _("The patient's hospitalizations:\n"), 509 caption = _('Editing hospitalizations ...'), 510 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')], 511 single_selection = True, 512 edit_callback = edit, 513 new_callback = edit, 514 delete_callback = delete, 515 refresh_callback = refresh, 516 list_tooltip_callback = get_tooltip 517 ) 518 519 #----------------------------------------------------------------
520 -def edit_hospital_stay(parent=None, hospital_stay=None):
521 ea = cHospitalStayEditAreaPnl(parent = parent, id = -1) 522 ea.data = hospital_stay 523 ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit') 524 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 525 dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospitalization'), _('Editing a hospitalization'))) 526 if dlg.ShowModal() == wx.ID_OK: 527 dlg.Destroy() 528 return True 529 dlg.Destroy() 530 return False
531 #----------------------------------------------------------------
532 -class cHospitalStayPhraseWheel(gmPhraseWheel.cPhraseWheel):
533 """Phrasewheel to allow selection of a hospitalization."""
534 - def __init__(self, *args, **kwargs):
535 536 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 537 538 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}} 539 540 mp = gmMatchProvider.cMatchProvider_SQL2 ( 541 queries = [ 542 u""" 543 select 544 pk_hospital_stay, 545 descr 546 from ( 547 select distinct on (pk_hospital_stay) 548 pk_hospital_stay, 549 descr 550 from 551 (select 552 pk_hospital_stay, 553 ( 554 to_char(admission, 'YYYY-Mon-DD') 555 || coalesce((' (' || hospital || '):'), ': ') 556 || episode 557 || coalesce((' (' || health_issue || ')'), '') 558 ) as descr 559 from 560 clin.v_pat_hospital_stays 561 where 562 %(ctxt_pat)s 563 564 hospital %(fragment_condition)s 565 or 566 episode %(fragment_condition)s 567 or 568 health_issue %(fragment_condition)s 569 ) as the_stays 570 ) as distinct_stays 571 order by descr 572 limit 25 573 """ ], 574 context = ctxt 575 ) 576 mp.setThresholds(3, 4, 6) 577 mp.set_context('pat', gmPerson.gmCurrentPatient().ID) 578 579 self.matcher = mp 580 self.selection_only = True
581 #---------------------------------------------------------------- 582 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl 583
584 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
585
586 - def __init__(self, *args, **kwargs):
589 #---------------------------------------------------------------- 590 # generic Edit Area mixin API 591 #----------------------------------------------------------------
592 - def _valid_for_save(self):
593 594 valid = True 595 596 if self._PRW_episode.GetValue().strip() == u'': 597 valid = False 598 self._PRW_episode.display_as_valid(False) 599 gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospitalization.'), beep = True) 600 self._PRW_episode.SetFocus() 601 602 if not self._PRW_admission.is_valid_timestamp(allow_empty = False): 603 valid = False 604 gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospitalization.'), beep = True) 605 self._PRW_admission.SetFocus() 606 607 if self._PRW_discharge.is_valid_timestamp(allow_empty = True): 608 if self._PRW_discharge.date is not None: 609 adm = self._PRW_admission.date 610 discharge = self._PRW_discharge.date 611 # normalize for comparison 612 discharge = discharge.replace ( 613 hour = adm.hour, 614 minute = adm.minute, 615 second = adm.second, 616 microsecond = adm.microsecond 617 ) 618 if adm is not None: 619 if discharge == adm: 620 self._PRW_discharge.SetData(discharge + pydt.timedelta(seconds = 1)) 621 elif not self._PRW_discharge.date > self._PRW_admission.date: 622 valid = False 623 self._PRW_discharge.display_as_valid(False) 624 gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospitalization.'), beep = True) 625 self._PRW_discharge.SetFocus() 626 627 return (valid is True)
628 #----------------------------------------------------------------
629 - def _save_as_new(self):
630 631 pat = gmPerson.gmCurrentPatient() 632 emr = pat.get_emr() 633 stay = emr.add_hospital_stay(episode = self._PRW_episode.GetData(can_create = True)) 634 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'') 635 stay['admission'] = self._PRW_admission.GetData() 636 stay['discharge'] = self._PRW_discharge.GetData() 637 stay.save_payload() 638 639 self.data = stay 640 return True
641 #----------------------------------------------------------------
642 - def _save_as_update(self):
643 644 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True) 645 self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'') 646 self.data['admission'] = self._PRW_admission.GetData() 647 self.data['discharge'] = self._PRW_discharge.GetData() 648 self.data.save_payload() 649 650 return True
651 #----------------------------------------------------------------
652 - def _refresh_as_new(self):
653 self._PRW_hospital.SetText(value = u'') 654 self._PRW_episode.SetText(value = u'') 655 self._PRW_admission.SetText(data = gmDateTime.pydt_now_here()) 656 self._PRW_discharge.SetText() 657 self._PRW_hospital.SetFocus()
658 #----------------------------------------------------------------
659 - def _refresh_from_existing(self):
660 if self.data['hospital'] is not None: 661 self._PRW_hospital.SetText(value = self.data['hospital']) 662 663 if self.data['pk_episode'] is not None: 664 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode']) 665 666 self._PRW_admission.SetText(data = self.data['admission']) 667 self._PRW_discharge.SetText(data = self.data['discharge']) 668 669 self._PRW_hospital.SetFocus()
670 #----------------------------------------------------------------
672 print "this was not expected to be used in this edit area"
673 674 #================================================================ 675 # encounter related widgets/functions 676 #----------------------------------------------------------------
677 -def start_new_encounter(emr=None):
678 emr.start_new_encounter() 679 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True) 680 time.sleep(0.5) 681 gmGuiHelpers.gm_show_info ( 682 _('\nA new encounter was started for the active patient.\n'), 683 _('Start of new encounter') 684 )
685 #---------------------------------------------------------------- 686 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg 687
688 -def edit_encounter(parent=None, encounter=None, msg=None):
689 if parent is None: 690 parent = wx.GetApp().GetTopWindow() 691 692 # FIXME: use generic dialog 2 693 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter, msg = msg) 694 if dlg.ShowModal() == wx.ID_OK: 695 dlg.Destroy() 696 return True 697 dlg.Destroy() 698 return False
699 #----------------------------------------------------------------
700 -def manage_encounters(**kwargs):
701 return select_encounters(**kwargs)
702
703 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
704 705 if patient is None: 706 patient = gmPerson.gmCurrentPatient() 707 708 if not patient.connected: 709 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 710 return False 711 712 if parent is None: 713 parent = wx.GetApp().GetTopWindow() 714 715 emr = patient.get_emr() 716 717 #-------------------- 718 def refresh(lctrl): 719 if encounters is None: 720 encs = emr.get_encounters() 721 else: 722 encs = encounters 723 724 items = [ 725 [ 726 gmDateTime.pydt_strftime(e['started'], '%Y %b %d %H:%M'), 727 e['last_affirmed'].strftime('%H:%M'), 728 e['l10n_type'], 729 gmTools.coalesce(e['reason_for_encounter'], u''), 730 gmTools.coalesce(e['assessment_of_encounter'], u''), 731 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin), 732 e['pk_encounter'] 733 ] for e in encs 734 ] 735 lctrl.set_string_items(items = items) 736 lctrl.set_data(data = encs) 737 active_pk = emr.active_encounter['pk_encounter'] 738 for idx in range(len(encs)): 739 e = encs[idx] 740 if e['pk_encounter'] == active_pk: 741 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
742 #-------------------- 743 def new(): 744 cfg_db = gmCfg.cCfgSQL() 745 # FIXME: look for MRU/MCU encounter type config here 746 enc_type = cfg_db.get2 ( 747 option = u'encounter.default_type', 748 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 749 bias = u'user', 750 default = u'in surgery' 751 ) 752 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type) 753 return edit_encounter(parent = parent, encounter = enc) 754 #-------------------- 755 def edit(enc=None): 756 return edit_encounter(parent = parent, encounter = enc) 757 #-------------------- 758 def edit_active(enc=None): 759 return edit_encounter(parent = parent, encounter = emr.active_encounter) 760 #-------------------- 761 def start_new(enc=None): 762 start_new_encounter(emr = emr) 763 return True 764 #-------------------- 765 return gmListWidgets.get_choices_from_list ( 766 parent = parent, 767 msg = _("The patient's encounters.\n"), 768 caption = _('Encounters ...'), 769 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'], 770 can_return_empty = False, 771 single_selection = single_selection, 772 refresh_callback = refresh, 773 edit_callback = edit, 774 new_callback = new, 775 ignore_OK_button = ignore_OK_button, 776 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active), 777 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new) 778 ) 779 #----------------------------------------------------------------
780 -def ask_for_encounter_continuation(msg=None, caption=None, encounter=None, parent=None):
781 """This is used as the callback when the EMR detects that the 782 patient was here rather recently and wants to ask the 783 provider whether to continue the recent encounter. 784 """ 785 if parent is None: 786 parent = wx.GetApp().GetTopWindow() 787 788 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 789 parent = None, 790 id = -1, 791 caption = caption, 792 question = msg, 793 button_defs = [ 794 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False}, 795 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True} 796 ], 797 show_checkbox = False 798 ) 799 800 result = dlg.ShowModal() 801 dlg.Destroy() 802 803 if result == wx.ID_YES: 804 return True 805 806 return False
807 #----------------------------------------------------------------
808 -def manage_encounter_types(parent=None):
809 810 if parent is None: 811 parent = wx.GetApp().GetTopWindow() 812 813 #-------------------- 814 def edit(enc_type=None): 815 return edit_encounter_type(parent = parent, encounter_type = enc_type)
816 #-------------------- 817 def delete(enc_type=None): 818 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']): 819 return True 820 gmDispatcher.send ( 821 signal = u'statustext', 822 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'], 823 beep = True 824 ) 825 return False 826 #-------------------- 827 def refresh(lctrl): 828 enc_types = gmEMRStructItems.get_encounter_types() 829 lctrl.set_string_items(items = enc_types) 830 #-------------------- 831 gmListWidgets.get_choices_from_list ( 832 parent = parent, 833 msg = _('\nSelect the encounter type you want to edit !\n'), 834 caption = _('Managing encounter types ...'), 835 columns = [_('Local name'), _('Encounter type')], 836 single_selection = True, 837 edit_callback = edit, 838 new_callback = edit, 839 delete_callback = delete, 840 refresh_callback = refresh 841 ) 842 #----------------------------------------------------------------
843 -def edit_encounter_type(parent=None, encounter_type=None):
844 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1) 845 ea.data = encounter_type 846 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit') 847 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea) 848 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name'))) 849 if dlg.ShowModal() == wx.ID_OK: 850 return True 851 return False
852 #----------------------------------------------------------------
853 -class cEncounterPhraseWheel(gmPhraseWheel.cPhraseWheel):
854
855 - def __init__(self, *args, **kwargs):
856 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 857 858 cmd = u""" 859 SELECT DISTINCT ON (list_label) 860 pk_encounter 861 AS data, 862 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type || ' [#' || pk_encounter || ']' 863 AS list_label, 864 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 865 AS field_label 866 FROM 867 clin.v_pat_encounters 868 WHERE 869 ( 870 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s 871 OR 872 l10n_type %(fragment_condition)s 873 OR 874 type %(fragment_condition)s 875 ) %(ctxt_patient)s 876 ORDER BY 877 list_label 878 LIMIT 879 30 880 """ 881 context = {'ctxt_patient': { 882 'where_part': u'AND pk_patient = %(patient)s', 883 'placeholder': u'patient' 884 }} 885 886 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context) 887 self.matcher._SQL_data2match = u""" 888 SELECT 889 pk_encounter 890 AS data, 891 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type 892 AS list_label, 893 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 894 AS field_label 895 FROM 896 clin.v_pat_encounters 897 WHERE 898 pk_encounter = %(pk)s 899 """ 900 self.matcher.setThresholds(1, 3, 5) 901 #self.matcher.print_queries = True 902 self.selection_only = True 903 # outside code MUST bind this to a patient 904 self.set_context(context = 'patient', val = None)
905 #--------------------------------------------------------
906 - def set_from_instance(self, instance):
907 val = u'%s: %s' % ( 908 gmDateTime.pydt_strftime(instance['started'], '%Y %b %d'), 909 instance['l10n_type'] 910 ) 911 self.SetText(value = val, data = instance['pk_encounter'])
912 #------------------------------------------------------------
913 - def _get_data_tooltip(self):
914 if self.GetData() is None: 915 return None 916 enc = gmEMRStructItems.cEncounter(aPK_obj = self._data.values()[0]['data']) 917 return enc.format ( 918 with_docs = False, 919 with_tests = False, 920 with_vaccinations = False, 921 with_family_history = False 922 )
923 #----------------------------------------------------------------
924 -class cEncounterTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
925 """Phrasewheel to allow selection of encounter type. 926 927 - user input interpreted as encounter type in English or local language 928 - data returned is pk of corresponding encounter type or None 929 """
930 - def __init__(self, *args, **kwargs):
931 932 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 933 934 mp = gmMatchProvider.cMatchProvider_SQL2 ( 935 queries = [ 936 u""" 937 SELECT 938 data, 939 field_label, 940 list_label 941 FROM ( 942 SELECT DISTINCT ON (data) * 943 FROM ( 944 SELECT 945 pk AS data, 946 _(description) AS field_label, 947 case 948 when _(description) = description then _(description) 949 else _(description) || ' (' || description || ')' 950 end AS list_label 951 FROM 952 clin.encounter_type 953 WHERE 954 _(description) %(fragment_condition)s 955 OR 956 description %(fragment_condition)s 957 ) AS q_distinct_pk 958 ) AS q_ordered 959 ORDER BY 960 list_label 961 """ ] 962 ) 963 mp.setThresholds(2, 4, 6) 964 965 self.matcher = mp 966 self.selection_only = True 967 self.picklist_delay = 50
968 #---------------------------------------------------------------- 969 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl 970
971 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
972
973 - def __init__(self, *args, **kwargs):
977 978 # self.__register_interests() 979 #------------------------------------------------------- 980 # generic edit area API 981 #-------------------------------------------------------
982 - def _valid_for_save(self):
983 if self.mode == 'edit': 984 if self._TCTRL_l10n_name.GetValue().strip() == u'': 985 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 986 return False 987 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 988 return True 989 990 no_errors = True 991 992 if self._TCTRL_l10n_name.GetValue().strip() == u'': 993 if self._TCTRL_name.GetValue().strip() == u'': 994 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 995 no_errors = False 996 else: 997 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 998 else: 999 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 1000 1001 if self._TCTRL_name.GetValue().strip() == u'': 1002 if self._TCTRL_l10n_name.GetValue().strip() == u'': 1003 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False) 1004 no_errors = False 1005 else: 1006 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 1007 else: 1008 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 1009 1010 return no_errors
1011 #-------------------------------------------------------
1012 - def _save_as_new(self):
1013 enc_type = gmEMRStructItems.create_encounter_type ( 1014 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''), 1015 l10n_description = gmTools.coalesce ( 1016 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''), 1017 self._TCTRL_name.GetValue().strip() 1018 ) 1019 ) 1020 if enc_type is None: 1021 return False 1022 self.data = enc_type 1023 return True
1024 #-------------------------------------------------------
1025 - def _save_as_update(self):
1026 enc_type = gmEMRStructItems.update_encounter_type ( 1027 description = self._TCTRL_name.GetValue().strip(), 1028 l10n_description = self._TCTRL_l10n_name.GetValue().strip() 1029 ) 1030 if enc_type is None: 1031 return False 1032 self.data = enc_type 1033 return True
1034 #-------------------------------------------------------
1035 - def _refresh_as_new(self):
1036 self._TCTRL_l10n_name.SetValue(u'') 1037 self._TCTRL_name.SetValue(u'') 1038 self._TCTRL_name.Enable(True)
1039 #-------------------------------------------------------
1040 - def _refresh_from_existing(self):
1041 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 1042 self._TCTRL_name.SetValue(self.data['description']) 1043 # disallow changing type on all encounters by editing system name 1044 self._TCTRL_name.Enable(False)
1045 #-------------------------------------------------------
1047 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 1048 self._TCTRL_name.SetValue(self.data['description']) 1049 self._TCTRL_name.Enable(True)
1050 #------------------------------------------------------- 1051 # internal API 1052 #------------------------------------------------------- 1053 # def __register_interests(self): 1054 # return 1055 #---------------------------------------------------------------- 1056 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl 1057
1058 -class cEncounterEditAreaPnl(wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl):
1059
1060 - def __init__(self, *args, **kwargs):
1061 try: 1062 self.__encounter = kwargs['encounter'] 1063 del kwargs['encounter'] 1064 except KeyError: 1065 self.__encounter = None 1066 1067 try: 1068 msg = kwargs['msg'] 1069 del kwargs['msg'] 1070 except KeyError: 1071 msg = None 1072 1073 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs) 1074 1075 self.refresh(msg = msg)
1076 #-------------------------------------------------------- 1077 # external API 1078 #--------------------------------------------------------
1079 - def refresh(self, encounter=None, msg=None):
1080 1081 if msg is not None: 1082 self._LBL_instructions.SetLabel(msg) 1083 1084 if encounter is not None: 1085 self.__encounter = encounter 1086 1087 if self.__encounter is None: 1088 return True 1089 1090 # getting the patient via the encounter allows us to act 1091 # on any encounter regardless of the currently active patient 1092 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient']) 1093 self._LBL_patient.SetLabel(pat.get_description_gender().strip()) 1094 curr_pat = gmPerson.gmCurrentPatient() 1095 if curr_pat.connected: 1096 if curr_pat.ID == self.__encounter['pk_patient']: 1097 self._LBL_patient.SetForegroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 1098 else: 1099 self._LBL_patient.SetForegroundColour('red') 1100 1101 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type']) 1102 1103 fts = gmDateTime.cFuzzyTimestamp ( 1104 timestamp = self.__encounter['started'], 1105 accuracy = gmDateTime.acc_minutes 1106 ) 1107 self._PRW_start.SetText(fts.format_accurately(), data=fts) 1108 1109 fts = gmDateTime.cFuzzyTimestamp ( 1110 timestamp = self.__encounter['last_affirmed'], 1111 accuracy = gmDateTime.acc_minutes 1112 ) 1113 self._PRW_end.SetText(fts.format_accurately(), data=fts) 1114 1115 # RFE 1116 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], '')) 1117 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe) 1118 self._PRW_rfe_codes.SetText(val, data) 1119 1120 # AOE 1121 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], '')) 1122 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe) 1123 self._PRW_aoe_codes.SetText(val, data) 1124 1125 # last affirmed 1126 if self.__encounter['last_affirmed'] == self.__encounter['started']: 1127 self._PRW_end.SetFocus() 1128 else: 1129 self._TCTRL_aoe.SetFocus() 1130 1131 return True
1132 #--------------------------------------------------------
1133 - def __is_valid_for_save(self):
1134 1135 if self._PRW_encounter_type.GetData() is None: 1136 self._PRW_encounter_type.SetBackgroundColour('pink') 1137 self._PRW_encounter_type.Refresh() 1138 self._PRW_encounter_type.SetFocus() 1139 return False 1140 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1141 self._PRW_encounter_type.Refresh() 1142 1143 # start 1144 if self._PRW_start.GetValue().strip() == u'': 1145 self._PRW_start.SetBackgroundColour('pink') 1146 self._PRW_start.Refresh() 1147 self._PRW_start.SetFocus() 1148 return False 1149 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False): 1150 self._PRW_start.SetBackgroundColour('pink') 1151 self._PRW_start.Refresh() 1152 self._PRW_start.SetFocus() 1153 return False 1154 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1155 self._PRW_start.Refresh() 1156 1157 # last_affirmed 1158 # if self._PRW_end.GetValue().strip() == u'': 1159 # self._PRW_end.SetBackgroundColour('pink') 1160 # self._PRW_end.Refresh() 1161 # self._PRW_end.SetFocus() 1162 # return False 1163 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False): 1164 self._PRW_end.SetBackgroundColour('pink') 1165 self._PRW_end.Refresh() 1166 self._PRW_end.SetFocus() 1167 return False 1168 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1169 self._PRW_end.Refresh() 1170 1171 return True
1172 #--------------------------------------------------------
1173 - def save(self):
1174 if not self.__is_valid_for_save(): 1175 return False 1176 1177 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData() 1178 self.__encounter['started'] = self._PRW_start.GetData().get_pydt() 1179 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt() 1180 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'') 1181 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'') 1182 self.__encounter.save_payload() # FIXME: error checking 1183 1184 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ] 1185 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ] 1186 1187 return True
1188 #---------------------------------------------------------------- 1189 # FIXME: use generic dialog 2
1190 -class cEncounterEditAreaDlg(wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg):
1191
1192 - def __init__(self, *args, **kwargs):
1193 encounter = kwargs['encounter'] 1194 del kwargs['encounter'] 1195 1196 try: 1197 button_defs = kwargs['button_defs'] 1198 del kwargs['button_defs'] 1199 except KeyError: 1200 button_defs = None 1201 1202 try: 1203 msg = kwargs['msg'] 1204 del kwargs['msg'] 1205 except KeyError: 1206 msg = None 1207 1208 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs) 1209 self.SetSize((450, 280)) 1210 self.SetMinSize((450, 280)) 1211 1212 if button_defs is not None: 1213 self._BTN_save.SetLabel(button_defs[0][0]) 1214 self._BTN_save.SetToolTipString(button_defs[0][1]) 1215 self._BTN_close.SetLabel(button_defs[1][0]) 1216 self._BTN_close.SetToolTipString(button_defs[1][1]) 1217 self.Refresh() 1218 1219 self._PNL_edit_area.refresh(encounter = encounter, msg = msg) 1220 1221 self.Fit()
1222 #--------------------------------------------------------
1223 - def _on_save_button_pressed(self, evt):
1224 if self._PNL_edit_area.save(): 1225 if self.IsModal(): 1226 self.EndModal(wx.ID_OK) 1227 else: 1228 self.Close()
1229 #--------------------------------------------------------
1231 start = self._PRW_encounter_start.GetData() 1232 if start is None: 1233 return 1234 start = start.get_pydt() 1235 1236 end = self._PRW_encounter_end.GetData() 1237 if end is None: 1238 fts = gmDateTime.cFuzzyTimestamp ( 1239 timestamp = start, 1240 accuracy = gmDateTime.acc_minutes 1241 ) 1242 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1243 return 1244 end = end.get_pydt() 1245 1246 if start > end: 1247 end = end.replace ( 1248 year = start.year, 1249 month = start.month, 1250 day = start.day 1251 ) 1252 fts = gmDateTime.cFuzzyTimestamp ( 1253 timestamp = end, 1254 accuracy = gmDateTime.acc_minutes 1255 ) 1256 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1257 return 1258 1259 emr = self.__pat.get_emr() 1260 if start != emr.active_encounter['started']: 1261 end = end.replace ( 1262 year = start.year, 1263 month = start.month, 1264 day = start.day 1265 ) 1266 fts = gmDateTime.cFuzzyTimestamp ( 1267 timestamp = end, 1268 accuracy = gmDateTime.acc_minutes 1269 ) 1270 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1271 return 1272 1273 return
1274 1275 #---------------------------------------------------------------- 1276 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl 1277
1278 -class cActiveEncounterPnl(wxgActiveEncounterPnl.wxgActiveEncounterPnl):
1279
1280 - def __init__(self, *args, **kwargs):
1281 wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs) 1282 self.__register_events() 1283 self.refresh()
1284 #------------------------------------------------------------
1285 - def clear(self):
1286 self._TCTRL_encounter.SetValue(u'') 1287 self._TCTRL_encounter.SetToolTipString(u'') 1288 self._BTN_new.Enable(False) 1289 self._BTN_list.Enable(False)
1290 #------------------------------------------------------------
1291 - def refresh(self):
1292 pat = gmPerson.gmCurrentPatient() 1293 if not pat.connected: 1294 self.clear() 1295 return 1296 1297 enc = pat.get_emr().active_encounter 1298 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n')) 1299 self._TCTRL_encounter.SetToolTipString ( 1300 _('The active encounter of the current patient:\n\n%s') % 1301 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n') 1302 ) 1303 self._BTN_new.Enable(True) 1304 self._BTN_list.Enable(True)
1305 #------------------------------------------------------------
1306 - def __register_events(self):
1307 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick) 1308 1309 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear) 1310 # this would throw an exception due to concurrency issues: 1311 #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_refresh) 1312 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh) 1313 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh) 1314 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1315 #------------------------------------------------------------ 1316 # event handler 1317 #------------------------------------------------------------
1318 - def _schedule_clear(self):
1319 wx.CallAfter(self.clear)
1320 #------------------------------------------------------------
1321 - def _schedule_refresh(self, *args, **kwargs):
1322 wx.CallAfter(self.refresh) 1323 return True
1324 #------------------------------------------------------------
1325 - def _on_ldclick(self, event):
1326 pat = gmPerson.gmCurrentPatient() 1327 if not pat.connected: 1328 return 1329 edit_encounter(encounter = pat.get_emr().active_encounter)
1330 #------------------------------------------------------------
1331 - def _on_new_button_pressed(self, event):
1332 pat = gmPerson.gmCurrentPatient() 1333 if not pat.connected: 1334 return 1335 start_new_encounter(emr = pat.get_emr())
1336 #------------------------------------------------------------
1337 - def _on_list_button_pressed(self, event):
1338 if not gmPerson.gmCurrentPatient().connected: 1339 return 1340 select_encounters()
1341 #================================================================ 1342 # episode related widgets/functions 1343 #----------------------------------------------------------------
1344 -def edit_episode(parent=None, episode=None):
1345 ea = cEpisodeEditAreaPnl(parent = parent, id = -1) 1346 ea.data = episode 1347 ea.mode = gmTools.coalesce(episode, 'new', 'edit') 1348 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1349 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode'))) 1350 if dlg.ShowModal() == wx.ID_OK: 1351 return True 1352 return False
1353 #----------------------------------------------------------------
1354 -def promote_episode_to_issue(parent=None, episode=None, emr=None):
1355 1356 created_new_issue = False 1357 1358 try: 1359 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient']) 1360 except gmExceptions.NoSuchBusinessObjectError: 1361 issue = None 1362 1363 if issue is None: 1364 issue = emr.add_health_issue(issue_name = episode['description']) 1365 created_new_issue = True 1366 else: 1367 # issue exists already, so ask user 1368 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1369 parent, 1370 -1, 1371 caption = _('Promoting episode to health issue'), 1372 question = _( 1373 'There already is a health issue\n' 1374 '\n' 1375 ' %s\n' 1376 '\n' 1377 'What do you want to do ?' 1378 ) % issue['description'], 1379 button_defs = [ 1380 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False}, 1381 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True} 1382 ] 1383 ) 1384 use_existing = dlg.ShowModal() 1385 dlg.Destroy() 1386 1387 if use_existing == wx.ID_CANCEL: 1388 return 1389 1390 # user wants to create new issue with alternate name 1391 if use_existing == wx.ID_NO: 1392 # loop until name modified but non-empty or cancelled 1393 issue_name = episode['description'] 1394 while issue_name == episode['description']: 1395 dlg = wx.TextEntryDialog ( 1396 parent = parent, 1397 message = _('Enter a short descriptive name for the new health issue:'), 1398 caption = _('Creating a new health issue ...'), 1399 defaultValue = issue_name, 1400 style = wx.OK | wx.CANCEL | wx.CENTRE 1401 ) 1402 decision = dlg.ShowModal() 1403 if decision != wx.ID_OK: 1404 dlg.Destroy() 1405 return 1406 issue_name = dlg.GetValue().strip() 1407 dlg.Destroy() 1408 if issue_name == u'': 1409 issue_name = episode['description'] 1410 1411 issue = emr.add_health_issue(issue_name = issue_name) 1412 created_new_issue = True 1413 1414 # eventually move the episode to the issue 1415 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True): 1416 # user cancelled the move so delete just-created issue 1417 if created_new_issue: 1418 # shouldn't fail as it is completely new 1419 gmEMRStructItems.delete_health_issue(health_issue = issue) 1420 return 1421 1422 return
1423 #----------------------------------------------------------------
1424 -def move_episode_to_issue(episode=None, target_issue=None, save_to_backend=False):
1425 """Prepare changing health issue for an episode. 1426 1427 Checks for two-open-episodes conflict. When this 1428 function succeeds, the pk_health_issue has been set 1429 on the episode instance and the episode should - for 1430 all practical purposes - be ready for save_payload(). 1431 """ 1432 # episode is closed: should always work 1433 if not episode['episode_open']: 1434 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1435 if save_to_backend: 1436 episode.save_payload() 1437 return True 1438 1439 # un-associate: should always work, too 1440 if target_issue is None: 1441 episode['pk_health_issue'] = None 1442 if save_to_backend: 1443 episode.save_payload() 1444 return True 1445 1446 # try closing possibly expired episode on target issue if any 1447 db_cfg = gmCfg.cCfgSQL() 1448 epi_ttl = int(db_cfg.get2 ( 1449 option = u'episode.ttl', 1450 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1451 bias = 'user', 1452 default = 60 # 2 months 1453 )) 1454 if target_issue.close_expired_episode(ttl=epi_ttl) is True: 1455 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description'])) 1456 existing_epi = target_issue.get_open_episode() 1457 1458 # no more open episode on target issue: should work now 1459 if existing_epi is None: 1460 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1461 if save_to_backend: 1462 episode.save_payload() 1463 return True 1464 1465 # don't conflict on SELF ;-) 1466 if existing_epi['pk_episode'] == episode['pk_episode']: 1467 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1468 if save_to_backend: 1469 episode.save_payload() 1470 return True 1471 1472 # we got two open episodes at once, ask user 1473 move_range = episode.get_access_range() 1474 exist_range = existing_epi.get_access_range() 1475 question = _( 1476 'You want to associate the running episode:\n\n' 1477 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n' 1478 'with the health issue:\n\n' 1479 ' "%(issue_name)s"\n\n' 1480 'There already is another episode running\n' 1481 'for this health issue:\n\n' 1482 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n' 1483 'However, there can only be one running\n' 1484 'episode per health issue.\n\n' 1485 'Which episode do you want to close ?' 1486 ) % { 1487 'new_epi_name': episode['description'], 1488 'new_epi_start': move_range[0].strftime('%m/%y'), 1489 'new_epi_end': move_range[1].strftime('%m/%y'), 1490 'issue_name': target_issue['description'], 1491 'old_epi_name': existing_epi['description'], 1492 'old_epi_start': exist_range[0].strftime('%m/%y'), 1493 'old_epi_end': exist_range[1].strftime('%m/%y') 1494 } 1495 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1496 parent = None, 1497 id = -1, 1498 caption = _('Resolving two-running-episodes conflict'), 1499 question = question, 1500 button_defs = [ 1501 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']}, 1502 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']} 1503 ] 1504 ) 1505 decision = dlg.ShowModal() 1506 1507 if decision == wx.ID_CANCEL: 1508 # button 3: move cancelled by user 1509 return False 1510 1511 elif decision == wx.ID_YES: 1512 # button 1: close old episode 1513 existing_epi['episode_open'] = False 1514 existing_epi.save_payload() 1515 1516 elif decision == wx.ID_NO: 1517 # button 2: close new episode 1518 episode['episode_open'] = False 1519 1520 else: 1521 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision) 1522 1523 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1524 if save_to_backend: 1525 episode.save_payload() 1526 return True
1527 #----------------------------------------------------------------
1528 -class cEpisodeListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1529 1530 # FIXME: support pre-selection 1531
1532 - def __init__(self, *args, **kwargs):
1533 1534 episodes = kwargs['episodes'] 1535 del kwargs['episodes'] 1536 1537 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1538 1539 self.SetTitle(_('Select the episodes you are interested in ...')) 1540 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')]) 1541 self._LCTRL_items.set_string_items ( 1542 items = [ 1543 [ epi['description'], 1544 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''), 1545 gmTools.coalesce(epi['health_issue'], u'') 1546 ] 1547 for epi in episodes ] 1548 ) 1549 self._LCTRL_items.set_column_widths() 1550 self._LCTRL_items.set_data(data = episodes)
1551 #----------------------------------------------------------------
1552 -class cEpisodeDescriptionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1553 """Let user select an episode *description*. 1554 1555 The user can select an episode description from the previously 1556 used descriptions across all episodes across all patients. 1557 1558 Selection is done with a phrasewheel so the user can 1559 type the episode name and matches will be shown. Typing 1560 "*" will show the entire list of episodes. 1561 1562 If the user types a description not existing yet a 1563 new episode description will be returned. 1564 """
1565 - def __init__(self, *args, **kwargs):
1566 1567 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1568 queries = [ 1569 u""" 1570 SELECT DISTINCT ON (description) 1571 description 1572 AS data, 1573 description 1574 AS field_label, 1575 description || ' (' 1576 || CASE 1577 WHEN is_open IS TRUE THEN _('ongoing') 1578 ELSE _('closed') 1579 END 1580 || ')' 1581 AS list_label 1582 FROM 1583 clin.episode 1584 WHERE 1585 description %(fragment_condition)s 1586 ORDER BY description 1587 LIMIT 30 1588 """ 1589 ] 1590 ) 1591 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1592 self.matcher = mp
1593 #----------------------------------------------------------------
1594 -class cEpisodeSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1595 """Let user select an episode. 1596 1597 The user can select an episode from the existing episodes of a 1598 patient. Selection is done with a phrasewheel so the user 1599 can type the episode name and matches will be shown. Typing 1600 "*" will show the entire list of episodes. Closed episodes 1601 will be marked as such. If the user types an episode name not 1602 in the list of existing episodes a new episode can be created 1603 from it if the programmer activated that feature. 1604 1605 If keyword <patient_id> is set to None or left out the control 1606 will listen to patient change signals and therefore act on 1607 gmPerson.gmCurrentPatient() changes. 1608 """
1609 - def __init__(self, *args, **kwargs):
1610 1611 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}} 1612 1613 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1614 queries = [ 1615 u"""( 1616 1617 select 1618 pk_episode 1619 as data, 1620 description 1621 as field_label, 1622 coalesce ( 1623 description || ' - ' || health_issue, 1624 description 1625 ) as list_label, 1626 1 as rank 1627 from 1628 clin.v_pat_episodes 1629 where 1630 episode_open is true and 1631 description %(fragment_condition)s 1632 %(ctxt_pat)s 1633 1634 ) union all ( 1635 1636 select 1637 pk_episode 1638 as data, 1639 description 1640 as field_label, 1641 coalesce ( 1642 description || _(' (closed)') || ' - ' || health_issue, 1643 description || _(' (closed)') 1644 ) as list_label, 1645 2 as rank 1646 from 1647 clin.v_pat_episodes 1648 where 1649 description %(fragment_condition)s and 1650 episode_open is false 1651 %(ctxt_pat)s 1652 1653 ) 1654 1655 order by rank, list_label 1656 limit 30""" 1657 ], 1658 context = ctxt 1659 ) 1660 1661 try: 1662 kwargs['patient_id'] 1663 except KeyError: 1664 kwargs['patient_id'] = None 1665 1666 if kwargs['patient_id'] is None: 1667 self.use_current_patient = True 1668 self.__register_patient_change_signals() 1669 pat = gmPerson.gmCurrentPatient() 1670 if pat.connected: 1671 mp.set_context('pat', pat.ID) 1672 else: 1673 self.use_current_patient = False 1674 self.__patient_id = int(kwargs['patient_id']) 1675 mp.set_context('pat', self.__patient_id) 1676 1677 del kwargs['patient_id'] 1678 1679 gmPhraseWheel.cPhraseWheel.__init__ ( 1680 self, 1681 *args, 1682 **kwargs 1683 ) 1684 self.matcher = mp
1685 #-------------------------------------------------------- 1686 # external API 1687 #--------------------------------------------------------
1688 - def set_patient(self, patient_id=None):
1689 if self.use_current_patient: 1690 return False 1691 self.__patient_id = int(patient_id) 1692 self.set_context('pat', self.__patient_id) 1693 return True
1694 #--------------------------------------------------------
1695 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1696 self.__is_open_for_create_data = is_open # used (only) in _create_data() 1697 return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1698 #--------------------------------------------------------
1699 - def _create_data(self):
1700 1701 epi_name = self.GetValue().strip() 1702 if epi_name == u'': 1703 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True) 1704 _log.debug('cannot create episode without name') 1705 return 1706 1707 if self.use_current_patient: 1708 pat = gmPerson.gmCurrentPatient() 1709 else: 1710 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 1711 1712 emr = pat.get_emr() 1713 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data) 1714 if epi is None: 1715 self.data = {} 1716 else: 1717 self.SetText ( 1718 value = epi_name, 1719 data = epi['pk_episode'] 1720 )
1721 #--------------------------------------------------------
1722 - def _data2instance(self):
1723 return gmEMRStructItems.cEpisode(aPK_obj = self.GetData())
1724 #-------------------------------------------------------- 1725 # internal API 1726 #--------------------------------------------------------
1728 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1729 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1730 #--------------------------------------------------------
1731 - def _pre_patient_selection(self):
1732 return True
1733 #--------------------------------------------------------
1734 - def _post_patient_selection(self):
1735 if self.use_current_patient: 1736 patient = gmPerson.gmCurrentPatient() 1737 self.set_context('pat', patient.ID) 1738 return True
1739 #---------------------------------------------------------------- 1740 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl 1741
1742 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1743
1744 - def __init__(self, *args, **kwargs):
1745 1746 try: 1747 episode = kwargs['episode'] 1748 del kwargs['episode'] 1749 except KeyError: 1750 episode = None 1751 1752 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs) 1753 gmEditArea.cGenericEditAreaMixin.__init__(self) 1754 1755 self.data = episode
1756 #---------------------------------------------------------------- 1757 # generic Edit Area mixin API 1758 #----------------------------------------------------------------
1759 - def _valid_for_save(self):
1760 1761 errors = False 1762 1763 if len(self._PRW_description.GetValue().strip()) == 0: 1764 errors = True 1765 self._PRW_description.display_as_valid(False) 1766 self._PRW_description.SetFocus() 1767 else: 1768 self._PRW_description.display_as_valid(True) 1769 self._PRW_description.Refresh() 1770 1771 return not errors
1772 #----------------------------------------------------------------
1773 - def _save_as_new(self):
1774 1775 pat = gmPerson.gmCurrentPatient() 1776 emr = pat.get_emr() 1777 1778 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip()) 1779 epi['summary'] = self._TCTRL_status.GetValue().strip() 1780 epi['episode_open'] = not self._CHBOX_closed.IsChecked() 1781 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 1782 1783 issue_name = self._PRW_issue.GetValue().strip() 1784 if len(issue_name) != 0: 1785 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1786 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue']) 1787 1788 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False): 1789 gmDispatcher.send ( 1790 signal = 'statustext', 1791 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1792 epi['description'], 1793 issue['description'] 1794 ) 1795 ) 1796 gmEMRStructItems.delete_episode(episode = epi) 1797 return False 1798 1799 epi.save() 1800 1801 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 1802 1803 self.data = epi 1804 return True
1805 #----------------------------------------------------------------
1806 - def _save_as_update(self):
1807 1808 self.data['description'] = self._PRW_description.GetValue().strip() 1809 self.data['summary'] = self._TCTRL_status.GetValue().strip() 1810 self.data['episode_open'] = not self._CHBOX_closed.IsChecked() 1811 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 1812 1813 issue_name = self._PRW_issue.GetValue().strip() 1814 if len(issue_name) == 0: 1815 self.data['pk_health_issue'] = None 1816 else: 1817 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1818 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue']) 1819 1820 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False): 1821 gmDispatcher.send ( 1822 signal = 'statustext', 1823 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1824 self.data['description'], 1825 issue['description'] 1826 ) 1827 ) 1828 return False 1829 1830 self.data.save() 1831 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 1832 1833 return True
1834 #----------------------------------------------------------------
1835 - def _refresh_as_new(self):
1836 if self.data is None: 1837 ident = gmPerson.gmCurrentPatient() 1838 else: 1839 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1840 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1841 self._PRW_issue.SetText() 1842 self._PRW_description.SetText() 1843 self._TCTRL_status.SetValue(u'') 1844 self._PRW_certainty.SetText() 1845 self._CHBOX_closed.SetValue(False) 1846 self._PRW_codes.SetText()
1847 #----------------------------------------------------------------
1848 - def _refresh_from_existing(self):
1849 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1850 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1851 1852 if self.data['pk_health_issue'] is not None: 1853 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue']) 1854 1855 self._PRW_description.SetText(self.data['description'], data=self.data['description']) 1856 1857 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u'')) 1858 1859 if self.data['diagnostic_certainty_classification'] is not None: 1860 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification']) 1861 1862 self._CHBOX_closed.SetValue(not self.data['episode_open']) 1863 1864 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 1865 self._PRW_codes.SetText(val, data)
1866 #----------------------------------------------------------------
1868 self._refresh_as_new()
1869 #================================================================ 1870 # health issue related widgets/functions 1871 #----------------------------------------------------------------
1872 -def edit_health_issue(parent=None, issue=None):
1873 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1) 1874 ea.data = issue 1875 ea.mode = gmTools.coalesce(issue, 'new', 'edit') 1876 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None)) 1877 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue'))) 1878 if dlg.ShowModal() == wx.ID_OK: 1879 return True 1880 return False
1881 #----------------------------------------------------------------
1882 -def select_health_issues(parent=None, emr=None):
1883 1884 if parent is None: 1885 parent = wx.GetApp().GetTopWindow() 1886 #----------------------------------------- 1887 def refresh(lctrl): 1888 issues = emr.get_health_issues() 1889 items = [ 1890 [ 1891 gmTools.bool2subst(i['is_confidential'], _('CONFIDENTIAL'), u'', u''), 1892 i['description'], 1893 gmTools.bool2subst(i['clinically_relevant'], _('relevant'), u'', u''), 1894 gmTools.bool2subst(i['is_active'], _('active'), u'', u''), 1895 gmTools.bool2subst(i['is_cause_of_death'], _('fatal'), u'', u'') 1896 ] for i in issues 1897 ] 1898 lctrl.set_string_items(items = items) 1899 lctrl.set_data(data = issues)
1900 #----------------------------------------- 1901 return gmListWidgets.get_choices_from_list ( 1902 parent = parent, 1903 msg = _('\nSelect the health issues !\n'), 1904 caption = _('Showing health issues ...'), 1905 columns = [u'', _('Health issue'), u'', u'', u''], 1906 single_selection = False, 1907 #edit_callback = edit, 1908 #new_callback = edit, 1909 #delete_callback = delete, 1910 refresh_callback = refresh 1911 ) 1912 #----------------------------------------------------------------
1913 -class cIssueListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1914 1915 # FIXME: support pre-selection 1916
1917 - def __init__(self, *args, **kwargs):
1918 1919 issues = kwargs['issues'] 1920 del kwargs['issues'] 1921 1922 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1923 1924 self.SetTitle(_('Select the health issues you are interested in ...')) 1925 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u'']) 1926 1927 for issue in issues: 1928 if issue['is_confidential']: 1929 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential')) 1930 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED')) 1931 else: 1932 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'') 1933 1934 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description']) 1935 if issue['clinically_relevant']: 1936 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant')) 1937 if issue['is_active']: 1938 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active')) 1939 if issue['is_cause_of_death']: 1940 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal')) 1941 1942 self._LCTRL_items.set_column_widths() 1943 self._LCTRL_items.set_data(data = issues)
1944 #----------------------------------------------------------------
1945 -class cIssueSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1946 """Let the user select a health issue. 1947 1948 The user can select a health issue from the existing issues 1949 of a patient. Selection is done with a phrasewheel so the user 1950 can type the issue name and matches will be shown. Typing 1951 "*" will show the entire list of issues. Inactive issues 1952 will be marked as such. If the user types an issue name not 1953 in the list of existing issues a new issue can be created 1954 from it if the programmer activated that feature. 1955 1956 If keyword <patient_id> is set to None or left out the control 1957 will listen to patient change signals and therefore act on 1958 gmPerson.gmCurrentPatient() changes. 1959 """
1960 - def __init__(self, *args, **kwargs):
1961 1962 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}} 1963 1964 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1965 # FIXME: consider clin.health_issue.clinically_relevant 1966 queries = [ 1967 u""" 1968 SELECT 1969 data, 1970 field_label, 1971 list_label 1972 FROM (( 1973 SELECT 1974 pk_health_issue AS data, 1975 description AS field_label, 1976 description AS list_label 1977 FROM clin.v_health_issues 1978 WHERE 1979 is_active IS true 1980 AND 1981 description %(fragment_condition)s 1982 AND 1983 %(ctxt_pat)s 1984 1985 ) UNION ( 1986 1987 SELECT 1988 pk_health_issue AS data, 1989 description AS field_label, 1990 description || _(' (inactive)') AS list_label 1991 FROM clin.v_health_issues 1992 WHERE 1993 is_active IS false 1994 AND 1995 description %(fragment_condition)s 1996 AND 1997 %(ctxt_pat)s 1998 )) AS union_query 1999 ORDER BY 2000 list_label"""], 2001 context = ctxt 2002 ) 2003 2004 try: kwargs['patient_id'] 2005 except KeyError: kwargs['patient_id'] = None 2006 2007 if kwargs['patient_id'] is None: 2008 self.use_current_patient = True 2009 self.__register_patient_change_signals() 2010 pat = gmPerson.gmCurrentPatient() 2011 if pat.connected: 2012 mp.set_context('pat', pat.ID) 2013 else: 2014 self.use_current_patient = False 2015 self.__patient_id = int(kwargs['patient_id']) 2016 mp.set_context('pat', self.__patient_id) 2017 2018 del kwargs['patient_id'] 2019 2020 gmPhraseWheel.cPhraseWheel.__init__ ( 2021 self, 2022 *args, 2023 **kwargs 2024 ) 2025 self.matcher = mp
2026 #-------------------------------------------------------- 2027 # external API 2028 #--------------------------------------------------------
2029 - def set_patient(self, patient_id=None):
2030 if self.use_current_patient: 2031 return False 2032 self.__patient_id = int(patient_id) 2033 self.set_context('pat', self.__patient_id) 2034 return True
2035 #--------------------------------------------------------
2036 - def _create_data(self):
2037 issue_name = self.GetValue().strip() 2038 if issue_name == u'': 2039 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create health issue without name.'), beep = True) 2040 _log.debug('cannot create health issue without name') 2041 return 2042 2043 if self.use_current_patient: 2044 pat = gmPerson.gmCurrentPatient() 2045 else: 2046 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 2047 2048 emr = pat.get_emr() 2049 issue = emr.add_health_issue(issue_name = issue_name) 2050 2051 if issue is None: 2052 self.data = {} 2053 else: 2054 self.SetText ( 2055 value = issue_name, 2056 data = issue['pk_health_issue'] 2057 )
2058 #--------------------------------------------------------
2059 - def _data2instance(self):
2060 return gmEMRStructItems.cHealthIssue(aPK_obj = self.GetData())
2061 #-------------------------------------------------------- 2062 # internal API 2063 #--------------------------------------------------------
2065 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 2066 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
2067 #--------------------------------------------------------
2068 - def _pre_patient_selection(self):
2069 return True
2070 #--------------------------------------------------------
2071 - def _post_patient_selection(self):
2072 if self.use_current_patient: 2073 patient = gmPerson.gmCurrentPatient() 2074 self.set_context('pat', patient.ID) 2075 return True
2076 #------------------------------------------------------------ 2077 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg 2078
2079 -class cIssueSelectionDlg(wxgIssueSelectionDlg.wxgIssueSelectionDlg):
2080
2081 - def __init__(self, *args, **kwargs):
2082 try: 2083 msg = kwargs['message'] 2084 except KeyError: 2085 msg = None 2086 del kwargs['message'] 2087 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs) 2088 if msg is not None: 2089 self._lbl_message.SetLabel(label=msg)
2090 #--------------------------------------------------------
2091 - def _on_OK_button_pressed(self, event):
2092 event.Skip() 2093 pk_issue = self._PhWheel_issue.GetData(can_create=True) 2094 if pk_issue is None: 2095 gmGuiHelpers.gm_show_error ( 2096 _('Cannot create new health issue:\n [%(issue)s]') % {'issue': self._PhWheel_issue.GetValue().strip()}, 2097 _('Selecting health issue') 2098 ) 2099 return False 2100 return True
2101 #------------------------------------------------------------ 2102 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl 2103
2104 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
2105 """Panel encapsulating health issue edit area functionality.""" 2106
2107 - def __init__(self, *args, **kwargs):
2108 2109 try: 2110 issue = kwargs['issue'] 2111 except KeyError: 2112 issue = None 2113 2114 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs) 2115 2116 gmEditArea.cGenericEditAreaMixin.__init__(self) 2117 2118 # FIXME: include more sources: coding systems/other database columns 2119 mp = gmMatchProvider.cMatchProvider_SQL2 ( 2120 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"] 2121 ) 2122 mp.setThresholds(1, 3, 5) 2123 self._PRW_condition.matcher = mp 2124 2125 mp = gmMatchProvider.cMatchProvider_SQL2 ( 2126 queries = [u""" 2127 select distinct on (grouping) grouping, grouping from ( 2128 2129 select rank, grouping from (( 2130 2131 select 2132 grouping, 2133 1 as rank 2134 from 2135 clin.health_issue 2136 where 2137 grouping %%(fragment_condition)s 2138 and 2139 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter) 2140 2141 ) union ( 2142 2143 select 2144 grouping, 2145 2 as rank 2146 from 2147 clin.health_issue 2148 where 2149 grouping %%(fragment_condition)s 2150 2151 )) as union_result 2152 2153 order by rank 2154 2155 ) as order_result 2156 2157 limit 50""" % gmPerson.gmCurrentPatient().ID 2158 ] 2159 ) 2160 mp.setThresholds(1, 3, 5) 2161 self._PRW_grouping.matcher = mp 2162 2163 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted) 2164 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted) 2165 2166 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted) 2167 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted) 2168 2169 self._PRW_year_noted.Enable(True) 2170 2171 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes) 2172 2173 self.data = issue
2174 #---------------------------------------------------------------- 2175 # generic Edit Area mixin API 2176 #----------------------------------------------------------------
2177 - def _valid_for_save(self):
2178 2179 if self._PRW_condition.GetValue().strip() == '': 2180 self._PRW_condition.display_as_valid(False) 2181 self._PRW_condition.SetFocus() 2182 return False 2183 self._PRW_condition.display_as_valid(True) 2184 self._PRW_condition.Refresh() 2185 2186 # FIXME: sanity check age/year diagnosed 2187 age_noted = self._PRW_age_noted.GetValue().strip() 2188 if age_noted != '': 2189 if gmDateTime.str2interval(str_interval = age_noted) is None: 2190 self._PRW_age_noted.display_as_valid(False) 2191 self._PRW_age_noted.SetFocus() 2192 return False 2193 self._PRW_age_noted.display_as_valid(True) 2194 2195 return True
2196 #----------------------------------------------------------------
2197 - def _save_as_new(self):
2198 pat = gmPerson.gmCurrentPatient() 2199 emr = pat.get_emr() 2200 2201 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip()) 2202 2203 side = u'' 2204 if self._ChBOX_left.GetValue(): 2205 side += u's' 2206 if self._ChBOX_right.GetValue(): 2207 side += u'd' 2208 issue['laterality'] = side 2209 2210 issue['summary'] = self._TCTRL_status.GetValue().strip() 2211 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 2212 issue['grouping'] = self._PRW_grouping.GetValue().strip() 2213 issue['is_active'] = self._ChBOX_active.GetValue() 2214 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue() 2215 issue['is_confidential'] = self._ChBOX_confidential.GetValue() 2216 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue() 2217 2218 age_noted = self._PRW_age_noted.GetData() 2219 if age_noted is not None: 2220 issue['age_noted'] = age_noted 2221 2222 issue.save() 2223 2224 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2225 2226 self.data = issue 2227 return True
2228 #----------------------------------------------------------------
2229 - def _save_as_update(self):
2230 2231 self.data['description'] = self._PRW_condition.GetValue().strip() 2232 2233 side = u'' 2234 if self._ChBOX_left.GetValue(): 2235 side += u's' 2236 if self._ChBOX_right.GetValue(): 2237 side += u'd' 2238 self.data['laterality'] = side 2239 2240 self.data['summary'] = self._TCTRL_status.GetValue().strip() 2241 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 2242 self.data['grouping'] = self._PRW_grouping.GetValue().strip() 2243 self.data['is_active'] = bool(self._ChBOX_active.GetValue()) 2244 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue()) 2245 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue()) 2246 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue()) 2247 2248 age_noted = self._PRW_age_noted.GetData() 2249 if age_noted is not None: 2250 self.data['age_noted'] = age_noted 2251 2252 self.data.save() 2253 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2254 2255 return True
2256 #----------------------------------------------------------------
2257 - def _refresh_as_new(self):
2258 self._PRW_condition.SetText() 2259 self._ChBOX_left.SetValue(0) 2260 self._ChBOX_right.SetValue(0) 2261 self._PRW_codes.SetText() 2262 self._on_leave_codes() 2263 self._PRW_certainty.SetText() 2264 self._PRW_grouping.SetText() 2265 self._TCTRL_status.SetValue(u'') 2266 self._PRW_age_noted.SetText() 2267 self._PRW_year_noted.SetText() 2268 self._ChBOX_active.SetValue(1) 2269 self._ChBOX_relevant.SetValue(1) 2270 self._ChBOX_confidential.SetValue(0) 2271 self._ChBOX_caused_death.SetValue(0) 2272 2273 return True
2274 #----------------------------------------------------------------
2275 - def _refresh_from_existing(self):
2276 self._PRW_condition.SetText(self.data['description']) 2277 2278 lat = gmTools.coalesce(self.data['laterality'], '') 2279 if lat.find('s') == -1: 2280 self._ChBOX_left.SetValue(0) 2281 else: 2282 self._ChBOX_left.SetValue(1) 2283 if lat.find('d') == -1: 2284 self._ChBOX_right.SetValue(0) 2285 else: 2286 self._ChBOX_right.SetValue(1) 2287 2288 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 2289 self._PRW_codes.SetText(val, data) 2290 self._on_leave_codes() 2291 2292 if self.data['diagnostic_certainty_classification'] is not None: 2293 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification']) 2294 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u'')) 2295 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u'')) 2296 2297 if self.data['age_noted'] is None: 2298 self._PRW_age_noted.SetText() 2299 else: 2300 self._PRW_age_noted.SetText ( 2301 value = '%sd' % self.data['age_noted'].days, 2302 data = self.data['age_noted'] 2303 ) 2304 2305 self._ChBOX_active.SetValue(self.data['is_active']) 2306 self._ChBOX_relevant.SetValue(self.data['clinically_relevant']) 2307 self._ChBOX_confidential.SetValue(self.data['is_confidential']) 2308 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death']) 2309 2310 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ... 2311 # self._PRW_age_noted.SetFocus() 2312 # self._PRW_condition.SetFocus() 2313 2314 return True
2315 #----------------------------------------------------------------
2317 return self._refresh_as_new()
2318 #-------------------------------------------------------- 2319 # internal helpers 2320 #--------------------------------------------------------
2321 - def _on_leave_codes(self, *args, **kwargs):
2322 if not self._PRW_codes.IsModified(): 2323 return True 2324 2325 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2326 #--------------------------------------------------------
2327 - def _on_leave_age_noted(self, *args, **kwargs):
2328 2329 if not self._PRW_age_noted.IsModified(): 2330 return True 2331 2332 str_age = self._PRW_age_noted.GetValue().strip() 2333 2334 if str_age == u'': 2335 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2336 return True 2337 2338 age = gmDateTime.str2interval(str_interval = str_age) 2339 2340 if age is None: 2341 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age) 2342 self._PRW_age_noted.SetBackgroundColour('pink') 2343 self._PRW_age_noted.Refresh() 2344 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2345 return True 2346 2347 pat = gmPerson.gmCurrentPatient() 2348 if pat['dob'] is not None: 2349 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob'] 2350 2351 if age >= max_age: 2352 gmDispatcher.send ( 2353 signal = 'statustext', 2354 msg = _( 2355 'Health issue cannot have been noted at age %s. Patient is only %s old.' 2356 ) % (age, pat.get_medical_age()) 2357 ) 2358 self._PRW_age_noted.SetBackgroundColour('pink') 2359 self._PRW_age_noted.Refresh() 2360 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2361 return True 2362 2363 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2364 self._PRW_age_noted.Refresh() 2365 self._PRW_age_noted.SetData(data=age) 2366 2367 if pat['dob'] is not None: 2368 fts = gmDateTime.cFuzzyTimestamp ( 2369 timestamp = pat['dob'] + age, 2370 accuracy = gmDateTime.acc_months 2371 ) 2372 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts) 2373 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB 2374 #wx.CallAfter(self._ChBOX_active.SetFocus) 2375 # if we do the following instead it will take us to the save/update button ... 2376 #wx.CallAfter(self.Navigate) 2377 2378 return True
2379 #--------------------------------------------------------
2380 - def _on_leave_year_noted(self, *args, **kwargs):
2381 2382 if not self._PRW_year_noted.IsModified(): 2383 return True 2384 2385 year_noted = self._PRW_year_noted.GetData() 2386 2387 if year_noted is None: 2388 if self._PRW_year_noted.GetValue().strip() == u'': 2389 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2390 return True 2391 self._PRW_year_noted.SetBackgroundColour('pink') 2392 self._PRW_year_noted.Refresh() 2393 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2394 return True 2395 2396 year_noted = year_noted.get_pydt() 2397 2398 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo): 2399 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.')) 2400 self._PRW_year_noted.SetBackgroundColour('pink') 2401 self._PRW_year_noted.Refresh() 2402 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2403 return True 2404 2405 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2406 self._PRW_year_noted.Refresh() 2407 2408 pat = gmPerson.gmCurrentPatient() 2409 if pat['dob'] is not None: 2410 issue_age = year_noted - pat['dob'] 2411 str_age = gmDateTime.format_interval_medically(interval = issue_age) 2412 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age) 2413 2414 return True
2415 #--------------------------------------------------------
2416 - def _on_modified_age_noted(self, *args, **kwargs):
2417 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2418 return True
2419 #--------------------------------------------------------
2420 - def _on_modified_year_noted(self, *args, **kwargs):
2421 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2422 return True
2423 #================================================================ 2424 # diagnostic certainty related widgets/functions 2425 #----------------------------------------------------------------
2426 -class cDiagnosticCertaintyClassificationPhraseWheel(gmPhraseWheel.cPhraseWheel):
2427
2428 - def __init__(self, *args, **kwargs):
2429 2430 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2431 2432 self.selection_only = False # can be NULL, too 2433 2434 mp = gmMatchProvider.cMatchProvider_FixedList ( 2435 aSeq = [ 2436 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1}, 2437 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1}, 2438 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1}, 2439 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1} 2440 ] 2441 ) 2442 mp.setThresholds(1, 2, 4) 2443 self.matcher = mp 2444 2445 self.SetToolTipString(_( 2446 "The diagnostic classification or grading of this assessment.\n" 2447 "\n" 2448 "This documents how certain one is about this being a true diagnosis." 2449 ))
2450 #================================================================ 2451 # MAIN 2452 #---------------------------------------------------------------- 2453 if __name__ == '__main__': 2454 2455 from Gnumed.business import gmPersonSearch 2456 from Gnumed.wxpython import gmPatSearchWidgets 2457 2458 #================================================================
2459 - class testapp (wx.App):
2460 """ 2461 Test application for testing EMR struct widgets 2462 """ 2463 #--------------------------------------------------------
2464 - def OnInit (self):
2465 """ 2466 Create test application UI 2467 """ 2468 frame = wx.Frame ( 2469 None, 2470 -4, 2471 'Testing EMR struct widgets', 2472 size=wx.Size(600, 400), 2473 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 2474 ) 2475 filemenu= wx.Menu() 2476 filemenu.AppendSeparator() 2477 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application") 2478 2479 # Creating the menubar. 2480 menuBar = wx.MenuBar() 2481 menuBar.Append(filemenu,"&File") 2482 2483 frame.SetMenuBar(menuBar) 2484 2485 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"), 2486 wx.DefaultPosition, wx.DefaultSize, 0 ) 2487 2488 # event handlers 2489 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow) 2490 2491 # patient EMR 2492 self.__pat = gmPerson.gmCurrentPatient() 2493 2494 frame.Show(1) 2495 return 1
2496 #--------------------------------------------------------
2497 - def OnCloseWindow (self, e):
2498 """ 2499 Close test aplication 2500 """ 2501 self.ExitMainLoop ()
2502 #----------------------------------------------------------------
2503 - def test_encounter_edit_area_panel():
2504 app = wx.PyWidgetTester(size = (200, 300)) 2505 emr = pat.get_emr() 2506 enc = emr.active_encounter 2507 #enc = gmEMRStructItems.cEncounter(1) 2508 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc) 2509 app.frame.Show(True) 2510 app.MainLoop() 2511 return
2512 #----------------------------------------------------------------
2513 - def test_encounter_edit_area_dialog():
2514 app = wx.PyWidgetTester(size = (200, 300)) 2515 emr = pat.get_emr() 2516 enc = emr.active_encounter 2517 #enc = gmEMRStructItems.cEncounter(1) 2518 2519 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc) 2520 dlg.ShowModal()
2521 2522 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc) 2523 # app.frame.Show(True) 2524 # app.MainLoop() 2525 #----------------------------------------------------------------
2526 - def test_epsiode_edit_area_pnl():
2527 app = wx.PyWidgetTester(size = (200, 300)) 2528 emr = pat.get_emr() 2529 epi = emr.get_episodes()[0] 2530 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi) 2531 app.frame.Show(True) 2532 app.MainLoop()
2533 #----------------------------------------------------------------
2534 - def test_episode_edit_area_dialog():
2535 app = wx.PyWidgetTester(size = (200, 300)) 2536 emr = pat.get_emr() 2537 epi = emr.get_episodes()[0] 2538 edit_episode(parent=app.frame, episode=epi)
2539 #----------------------------------------------------------------
2540 - def test_hospital_stay_prw():
2541 app = wx.PyWidgetTester(size = (400, 40)) 2542 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2543 app.MainLoop()
2544 #----------------------------------------------------------------
2545 - def test_episode_selection_prw():
2546 app = wx.PyWidgetTester(size = (400, 40)) 2547 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2548 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID) 2549 app.MainLoop()
2550 #----------------------------------------------------------------
2551 - def test_health_issue_edit_area_dlg():
2552 app = wx.PyWidgetTester(size = (200, 300)) 2553 edit_health_issue(parent=app.frame, issue=None)
2554 #----------------------------------------------------------------
2555 - def test_health_issue_edit_area_pnl():
2556 app = wx.PyWidgetTester(size = (200, 300)) 2557 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400)) 2558 app.MainLoop()
2559 #----------------------------------------------------------------
2560 - def test_edit_procedure():
2561 app = wx.PyWidgetTester(size = (200, 300)) 2562 edit_procedure(parent=app.frame)
2563 #================================================================ 2564 2565 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 2566 2567 gmI18N.activate_locale() 2568 gmI18N.install_domain() 2569 gmDateTime.init() 2570 2571 # obtain patient 2572 pat = gmPersonSearch.ask_for_patient() 2573 if pat is None: 2574 print "No patient. Exiting gracefully..." 2575 sys.exit(0) 2576 gmPatSearchWidgets.set_active_patient(patient=pat) 2577 2578 # try: 2579 # lauch emr dialogs test application 2580 # app = testapp(0) 2581 # app.MainLoop() 2582 # except StandardError: 2583 # _log.exception("unhandled exception caught !") 2584 # but re-raise them 2585 # raise 2586 2587 #test_encounter_edit_area_panel() 2588 #test_encounter_edit_area_dialog() 2589 #test_epsiode_edit_area_pnl() 2590 #test_episode_edit_area_dialog() 2591 #test_health_issue_edit_area_dlg() 2592 #test_episode_selection_prw() 2593 #test_hospital_stay_prw() 2594 test_edit_procedure() 2595 2596 #================================================================ 2597