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 new(): 719 cfg_db = gmCfg.cCfgSQL() 720 enc_type = cfg_db.get2 ( 721 option = u'encounter.default_type', 722 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 723 bias = u'user' 724 ) 725 if enc_type is None: 726 enc_type = gmEMRStructItems.get_most_commonly_used_encounter_type() 727 if enc_type is None: 728 enc_type = u'in surgery' 729 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type) 730 return edit_encounter(parent = parent, encounter = enc)
731 #-------------------- 732 def edit(enc=None): 733 return edit_encounter(parent = parent, encounter = enc) 734 #-------------------- 735 def edit_active(enc=None): 736 return edit_encounter(parent = parent, encounter = emr.active_encounter) 737 #-------------------- 738 def start_new(enc=None): 739 start_new_encounter(emr = emr) 740 return True 741 #-------------------- 742 def get_tooltip(data): 743 if data is None: 744 return None 745 return data.format ( 746 patient = patient, 747 with_soap = False, 748 with_docs = False, 749 with_tests = False, 750 with_vaccinations = False, 751 with_rfe_aoe = True, 752 with_family_history = False, 753 by_episode=False, 754 fancy_header = True, 755 ) 756 #-------------------- 757 def refresh(lctrl): 758 if encounters is None: 759 encs = emr.get_encounters() 760 else: 761 encs = encounters 762 763 items = [ 764 [ 765 u'%s - %s' % (gmDateTime.pydt_strftime(e['started'], '%Y %b %d %H:%M'), e['last_affirmed'].strftime('%H:%M')), 766 e['l10n_type'], 767 gmTools.coalesce(e['praxis_branch'], u''), 768 gmTools.coalesce(e['reason_for_encounter'], u''), 769 gmTools.coalesce(e['assessment_of_encounter'], u''), 770 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin), 771 e['pk_encounter'] 772 ] for e in encs 773 ] 774 lctrl.set_string_items(items = items) 775 lctrl.set_data(data = encs) 776 active_pk = emr.active_encounter['pk_encounter'] 777 for idx in range(len(encs)): 778 e = encs[idx] 779 if e['pk_encounter'] == active_pk: 780 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED')) 781 #-------------------- 782 return gmListWidgets.get_choices_from_list ( 783 parent = parent, 784 msg = _("The patient's encounters.\n"), 785 caption = _('Encounters ...'), 786 columns = [_('When'), _('Type'), _('Where'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'], 787 can_return_empty = False, 788 single_selection = single_selection, 789 refresh_callback = refresh, 790 edit_callback = edit, 791 new_callback = new, 792 list_tooltip_callback = get_tooltip, 793 ignore_OK_button = ignore_OK_button, 794 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active), 795 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new) 796 ) 797 798 #----------------------------------------------------------------
799 -def ask_for_encounter_continuation(msg=None, caption=None, encounter=None, parent=None):
800 """This is used as the callback when the EMR detects that the 801 patient was here rather recently and wants to ask the 802 provider whether to continue the recent encounter. 803 """ 804 if parent is None: 805 parent = wx.GetApp().GetTopWindow() 806 807 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 808 parent = None, 809 id = -1, 810 caption = caption, 811 question = msg, 812 button_defs = [ 813 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False}, 814 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True} 815 ], 816 show_checkbox = False 817 ) 818 819 result = dlg.ShowModal() 820 dlg.Destroy() 821 822 if result == wx.ID_YES: 823 return True 824 825 return False
826 #----------------------------------------------------------------
827 -def manage_encounter_types(parent=None):
828 829 if parent is None: 830 parent = wx.GetApp().GetTopWindow() 831 832 #-------------------- 833 def edit(enc_type=None): 834 return edit_encounter_type(parent = parent, encounter_type = enc_type)
835 #-------------------- 836 def delete(enc_type=None): 837 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']): 838 return True 839 gmDispatcher.send ( 840 signal = u'statustext', 841 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'], 842 beep = True 843 ) 844 return False 845 #-------------------- 846 def refresh(lctrl): 847 enc_types = gmEMRStructItems.get_encounter_types() 848 lctrl.set_string_items(items = enc_types) 849 #-------------------- 850 gmListWidgets.get_choices_from_list ( 851 parent = parent, 852 msg = _('\nSelect the encounter type you want to edit !\n'), 853 caption = _('Managing encounter types ...'), 854 columns = [_('Local name'), _('Encounter type')], 855 single_selection = True, 856 edit_callback = edit, 857 new_callback = edit, 858 delete_callback = delete, 859 refresh_callback = refresh 860 ) 861 #----------------------------------------------------------------
862 -def edit_encounter_type(parent=None, encounter_type=None):
863 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1) 864 ea.data = encounter_type 865 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit') 866 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea) 867 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name'))) 868 if dlg.ShowModal() == wx.ID_OK: 869 return True 870 return False
871 #----------------------------------------------------------------
872 -class cEncounterPhraseWheel(gmPhraseWheel.cPhraseWheel):
873
874 - def __init__(self, *args, **kwargs):
875 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 876 877 cmd = u""" 878 SELECT DISTINCT ON (list_label) 879 pk_encounter 880 AS data, 881 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type || ' [#' || pk_encounter || ']' 882 AS list_label, 883 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 884 AS field_label 885 FROM 886 clin.v_pat_encounters 887 WHERE 888 ( 889 to_char(started, 'YYYY-MM-DD') %(fragment_condition)s 890 OR 891 l10n_type %(fragment_condition)s 892 OR 893 type %(fragment_condition)s 894 ) %(ctxt_patient)s 895 ORDER BY 896 list_label 897 LIMIT 898 30 899 """ 900 context = {'ctxt_patient': { 901 'where_part': u'AND pk_patient = %(patient)s', 902 'placeholder': u'patient' 903 }} 904 905 self.matcher = gmMatchProvider.cMatchProvider_SQL2(queries = [cmd], context = context) 906 self.matcher._SQL_data2match = u""" 907 SELECT 908 pk_encounter 909 AS data, 910 to_char(started, 'YYYY Mon DD (HH24:MI)') || ': ' || l10n_type 911 AS list_label, 912 to_char(started, 'YYYY Mon DD') || ': ' || l10n_type 913 AS field_label 914 FROM 915 clin.v_pat_encounters 916 WHERE 917 pk_encounter = %(pk)s 918 """ 919 self.matcher.setThresholds(1, 3, 5) 920 #self.matcher.print_queries = True 921 self.selection_only = True 922 # outside code MUST bind this to a patient 923 self.set_context(context = 'patient', val = None)
924 #--------------------------------------------------------
925 - def set_from_instance(self, instance):
926 val = u'%s: %s' % ( 927 gmDateTime.pydt_strftime(instance['started'], '%Y %b %d'), 928 instance['l10n_type'] 929 ) 930 self.SetText(value = val, data = instance['pk_encounter'])
931 #------------------------------------------------------------
932 - def _get_data_tooltip(self):
933 if self.GetData() is None: 934 return None 935 enc = gmEMRStructItems.cEncounter(aPK_obj = self._data.values()[0]['data']) 936 return enc.format ( 937 with_docs = False, 938 with_tests = False, 939 with_vaccinations = False, 940 with_family_history = False 941 )
942 #----------------------------------------------------------------
943 -class cEncounterTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
944 """Phrasewheel to allow selection of encounter type. 945 946 - user input interpreted as encounter type in English or local language 947 - data returned is pk of corresponding encounter type or None 948 """
949 - def __init__(self, *args, **kwargs):
950 951 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 952 953 mp = gmMatchProvider.cMatchProvider_SQL2 ( 954 queries = [ 955 u""" 956 SELECT 957 data, 958 field_label, 959 list_label 960 FROM ( 961 SELECT DISTINCT ON (data) * 962 FROM ( 963 SELECT 964 pk AS data, 965 _(description) AS field_label, 966 case 967 when _(description) = description then _(description) 968 else _(description) || ' (' || description || ')' 969 end AS list_label 970 FROM 971 clin.encounter_type 972 WHERE 973 _(description) %(fragment_condition)s 974 OR 975 description %(fragment_condition)s 976 ) AS q_distinct_pk 977 ) AS q_ordered 978 ORDER BY 979 list_label 980 """ ] 981 ) 982 mp.setThresholds(2, 4, 6) 983 984 self.matcher = mp 985 self.selection_only = True 986 self.picklist_delay = 50
987 #---------------------------------------------------------------- 988 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl 989
990 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
991
992 - def __init__(self, *args, **kwargs):
996 997 # self.__register_interests() 998 #------------------------------------------------------- 999 # generic edit area API 1000 #-------------------------------------------------------
1001 - def _valid_for_save(self):
1002 if self.mode == 'edit': 1003 if self._TCTRL_l10n_name.GetValue().strip() == u'': 1004 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 1005 return False 1006 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 1007 return True 1008 1009 no_errors = True 1010 1011 if self._TCTRL_l10n_name.GetValue().strip() == u'': 1012 if self._TCTRL_name.GetValue().strip() == u'': 1013 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 1014 no_errors = False 1015 else: 1016 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 1017 else: 1018 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 1019 1020 if self._TCTRL_name.GetValue().strip() == u'': 1021 if self._TCTRL_l10n_name.GetValue().strip() == u'': 1022 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False) 1023 no_errors = False 1024 else: 1025 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 1026 else: 1027 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 1028 1029 return no_errors
1030 #-------------------------------------------------------
1031 - def _save_as_new(self):
1032 enc_type = gmEMRStructItems.create_encounter_type ( 1033 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''), 1034 l10n_description = gmTools.coalesce ( 1035 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''), 1036 self._TCTRL_name.GetValue().strip() 1037 ) 1038 ) 1039 if enc_type is None: 1040 return False 1041 self.data = enc_type 1042 return True
1043 #-------------------------------------------------------
1044 - def _save_as_update(self):
1045 enc_type = gmEMRStructItems.update_encounter_type ( 1046 description = self._TCTRL_name.GetValue().strip(), 1047 l10n_description = self._TCTRL_l10n_name.GetValue().strip() 1048 ) 1049 if enc_type is None: 1050 return False 1051 self.data = enc_type 1052 return True
1053 #-------------------------------------------------------
1054 - def _refresh_as_new(self):
1055 self._TCTRL_l10n_name.SetValue(u'') 1056 self._TCTRL_name.SetValue(u'') 1057 self._TCTRL_name.Enable(True)
1058 #-------------------------------------------------------
1059 - def _refresh_from_existing(self):
1060 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 1061 self._TCTRL_name.SetValue(self.data['description']) 1062 # disallow changing type on all encounters by editing system name 1063 self._TCTRL_name.Enable(False)
1064 #-------------------------------------------------------
1066 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 1067 self._TCTRL_name.SetValue(self.data['description']) 1068 self._TCTRL_name.Enable(True)
1069 #------------------------------------------------------- 1070 # internal API 1071 #------------------------------------------------------- 1072 # def __register_interests(self): 1073 # return 1074 #---------------------------------------------------------------- 1075 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl 1076
1077 -class cEncounterEditAreaPnl(wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl):
1078
1079 - def __init__(self, *args, **kwargs):
1080 try: 1081 self.__encounter = kwargs['encounter'] 1082 del kwargs['encounter'] 1083 except KeyError: 1084 self.__encounter = None 1085 1086 try: 1087 msg = kwargs['msg'] 1088 del kwargs['msg'] 1089 except KeyError: 1090 msg = None 1091 1092 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs) 1093 1094 self.refresh(msg = msg)
1095 #-------------------------------------------------------- 1096 # external API 1097 #--------------------------------------------------------
1098 - def refresh(self, encounter=None, msg=None):
1099 1100 if msg is not None: 1101 self._LBL_instructions.SetLabel(msg) 1102 1103 if encounter is not None: 1104 self.__encounter = encounter 1105 1106 if self.__encounter is None: 1107 return True 1108 1109 # getting the patient via the encounter allows us to act 1110 # on any encounter regardless of the currently active patient 1111 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient']) 1112 self._LBL_patient.SetLabel(pat.get_description_gender().strip()) 1113 curr_pat = gmPerson.gmCurrentPatient() 1114 if curr_pat.connected: 1115 if curr_pat.ID == self.__encounter['pk_patient']: 1116 self._LBL_patient.SetForegroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 1117 else: 1118 self._LBL_patient.SetForegroundColour('red') 1119 1120 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data = self.__encounter['pk_type']) 1121 self._PRW_location.Enable(True) 1122 self._PRW_location.display_as_disabled(False) 1123 branch = self.__encounter.praxis_branch 1124 if branch is None: # None or old entry because praxis has been re-configured 1125 unit = self.__encounter.org_unit 1126 if unit is None: # None 1127 self._PRW_location.SetText(u'', data = None) 1128 else: # old entry 1129 self._PRW_location.Enable(False) 1130 self._PRW_location.display_as_disabled(True) 1131 self._PRW_location.SetText(_('old praxis branch: %s (%s)') % (unit['unit'], unit['organization']), data = None) 1132 else: 1133 self._PRW_location.SetText(self.__encounter['praxis_branch'], data = branch['pk_praxis_branch']) 1134 1135 fts = gmDateTime.cFuzzyTimestamp ( 1136 timestamp = self.__encounter['started'], 1137 accuracy = gmDateTime.acc_minutes 1138 ) 1139 self._PRW_start.SetText(fts.format_accurately(), data=fts) 1140 1141 fts = gmDateTime.cFuzzyTimestamp ( 1142 timestamp = self.__encounter['last_affirmed'], 1143 accuracy = gmDateTime.acc_minutes 1144 ) 1145 self._PRW_end.SetText(fts.format_accurately(), data=fts) 1146 1147 # RFE 1148 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], '')) 1149 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe) 1150 self._PRW_rfe_codes.SetText(val, data) 1151 1152 # AOE 1153 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], '')) 1154 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe) 1155 self._PRW_aoe_codes.SetText(val, data) 1156 1157 # last affirmed 1158 if self.__encounter['last_affirmed'] == self.__encounter['started']: 1159 self._PRW_end.SetFocus() 1160 else: 1161 self._TCTRL_aoe.SetFocus() 1162 1163 return True
1164 #--------------------------------------------------------
1165 - def __is_valid_for_save(self):
1166 1167 if self._PRW_encounter_type.GetData() is None: 1168 self._PRW_encounter_type.SetBackgroundColour('pink') 1169 self._PRW_encounter_type.Refresh() 1170 self._PRW_encounter_type.SetFocus() 1171 return False 1172 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1173 self._PRW_encounter_type.Refresh() 1174 1175 # start 1176 if self._PRW_start.GetValue().strip() == u'': 1177 self._PRW_start.SetBackgroundColour('pink') 1178 self._PRW_start.Refresh() 1179 self._PRW_start.SetFocus() 1180 return False 1181 if not self._PRW_start.is_valid_timestamp(empty_is_valid = False): 1182 self._PRW_start.SetBackgroundColour('pink') 1183 self._PRW_start.Refresh() 1184 self._PRW_start.SetFocus() 1185 return False 1186 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1187 self._PRW_start.Refresh() 1188 1189 # last_affirmed 1190 # if self._PRW_end.GetValue().strip() == u'': 1191 # self._PRW_end.SetBackgroundColour('pink') 1192 # self._PRW_end.Refresh() 1193 # self._PRW_end.SetFocus() 1194 # return False 1195 if not self._PRW_end.is_valid_timestamp(empty_is_valid = False): 1196 self._PRW_end.SetBackgroundColour('pink') 1197 self._PRW_end.Refresh() 1198 self._PRW_end.SetFocus() 1199 return False 1200 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1201 self._PRW_end.Refresh() 1202 1203 return True
1204 #--------------------------------------------------------
1205 - def save(self):
1206 if not self.__is_valid_for_save(): 1207 return False 1208 1209 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData() 1210 self.__encounter['started'] = self._PRW_start.GetData().get_pydt() 1211 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt() 1212 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'') 1213 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'') 1214 self.__encounter.save_payload() # FIXME: error checking 1215 1216 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ] 1217 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ] 1218 1219 return True
1220 #---------------------------------------------------------------- 1221 # FIXME: use generic dialog 2
1222 -class cEncounterEditAreaDlg(wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg):
1223
1224 - def __init__(self, *args, **kwargs):
1225 encounter = kwargs['encounter'] 1226 del kwargs['encounter'] 1227 1228 try: 1229 button_defs = kwargs['button_defs'] 1230 del kwargs['button_defs'] 1231 except KeyError: 1232 button_defs = None 1233 1234 try: 1235 msg = kwargs['msg'] 1236 del kwargs['msg'] 1237 except KeyError: 1238 msg = None 1239 1240 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs) 1241 self.SetSize((450, 280)) 1242 self.SetMinSize((450, 280)) 1243 1244 if button_defs is not None: 1245 self._BTN_save.SetLabel(button_defs[0][0]) 1246 self._BTN_save.SetToolTipString(button_defs[0][1]) 1247 self._BTN_close.SetLabel(button_defs[1][0]) 1248 self._BTN_close.SetToolTipString(button_defs[1][1]) 1249 self.Refresh() 1250 1251 self._PNL_edit_area.refresh(encounter = encounter, msg = msg) 1252 1253 self.Fit()
1254 #--------------------------------------------------------
1255 - def _on_save_button_pressed(self, evt):
1256 if self._PNL_edit_area.save(): 1257 if self.IsModal(): 1258 self.EndModal(wx.ID_OK) 1259 else: 1260 self.Close()
1261 #--------------------------------------------------------
1263 start = self._PRW_encounter_start.GetData() 1264 if start is None: 1265 return 1266 start = start.get_pydt() 1267 1268 end = self._PRW_encounter_end.GetData() 1269 if end is None: 1270 fts = gmDateTime.cFuzzyTimestamp ( 1271 timestamp = start, 1272 accuracy = gmDateTime.acc_minutes 1273 ) 1274 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1275 return 1276 end = end.get_pydt() 1277 1278 if start > end: 1279 end = end.replace ( 1280 year = start.year, 1281 month = start.month, 1282 day = start.day 1283 ) 1284 fts = gmDateTime.cFuzzyTimestamp ( 1285 timestamp = end, 1286 accuracy = gmDateTime.acc_minutes 1287 ) 1288 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1289 return 1290 1291 emr = self.__pat.get_emr() 1292 if start != emr.active_encounter['started']: 1293 end = end.replace ( 1294 year = start.year, 1295 month = start.month, 1296 day = start.day 1297 ) 1298 fts = gmDateTime.cFuzzyTimestamp ( 1299 timestamp = end, 1300 accuracy = gmDateTime.acc_minutes 1301 ) 1302 self._PRW_encounter_end.SetText(fts.format_accurately(), data = fts) 1303 return 1304 1305 return
1306 1307 #---------------------------------------------------------------- 1308 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl 1309
1310 -class cActiveEncounterPnl(wxgActiveEncounterPnl.wxgActiveEncounterPnl):
1311
1312 - def __init__(self, *args, **kwargs):
1313 wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs) 1314 self.__register_events() 1315 self.refresh()
1316 #------------------------------------------------------------
1317 - def clear(self):
1318 self._TCTRL_encounter.SetValue(u'') 1319 self._TCTRL_encounter.SetToolTipString(u'') 1320 self._BTN_new.Enable(False) 1321 self._BTN_list.Enable(False)
1322 #------------------------------------------------------------
1323 - def refresh(self):
1324 pat = gmPerson.gmCurrentPatient() 1325 if not pat.connected: 1326 self.clear() 1327 return 1328 1329 enc = pat.get_emr().active_encounter 1330 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n')) 1331 self._TCTRL_encounter.SetToolTipString ( 1332 _('The active encounter of the current patient:\n\n%s') % 1333 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n') 1334 ) 1335 self._BTN_new.Enable(True) 1336 self._BTN_list.Enable(True)
1337 #------------------------------------------------------------
1338 - def __register_events(self):
1339 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick) 1340 1341 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear) 1342 # this would throw an exception due to concurrency issues: 1343 #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_refresh) 1344 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh) 1345 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh) 1346 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1347 #------------------------------------------------------------ 1348 # event handler 1349 #------------------------------------------------------------
1350 - def _schedule_clear(self):
1351 wx.CallAfter(self.clear)
1352 #------------------------------------------------------------
1353 - def _schedule_refresh(self, *args, **kwargs):
1354 wx.CallAfter(self.refresh) 1355 return True
1356 #------------------------------------------------------------
1357 - def _on_ldclick(self, event):
1358 pat = gmPerson.gmCurrentPatient() 1359 if not pat.connected: 1360 return 1361 edit_encounter(encounter = pat.get_emr().active_encounter)
1362 #------------------------------------------------------------
1363 - def _on_new_button_pressed(self, event):
1364 pat = gmPerson.gmCurrentPatient() 1365 if not pat.connected: 1366 return 1367 start_new_encounter(emr = pat.get_emr())
1368 #------------------------------------------------------------
1369 - def _on_list_button_pressed(self, event):
1370 if not gmPerson.gmCurrentPatient().connected: 1371 return 1372 select_encounters()
1373 #================================================================ 1374 # episode related widgets/functions 1375 #----------------------------------------------------------------
1376 -def edit_episode(parent=None, episode=None):
1377 ea = cEpisodeEditAreaPnl(parent = parent, id = -1) 1378 ea.data = episode 1379 ea.mode = gmTools.coalesce(episode, 'new', 'edit') 1380 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1381 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode'))) 1382 if dlg.ShowModal() == wx.ID_OK: 1383 return True 1384 return False
1385 #----------------------------------------------------------------
1386 -def promote_episode_to_issue(parent=None, episode=None, emr=None):
1387 1388 created_new_issue = False 1389 1390 try: 1391 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient']) 1392 except gmExceptions.NoSuchBusinessObjectError: 1393 issue = None 1394 1395 if issue is None: 1396 issue = emr.add_health_issue(issue_name = episode['description']) 1397 created_new_issue = True 1398 else: 1399 # issue exists already, so ask user 1400 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1401 parent, 1402 -1, 1403 caption = _('Promoting episode to health issue'), 1404 question = _( 1405 'There already is a health issue\n' 1406 '\n' 1407 ' %s\n' 1408 '\n' 1409 'What do you want to do ?' 1410 ) % issue['description'], 1411 button_defs = [ 1412 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False}, 1413 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True} 1414 ] 1415 ) 1416 use_existing = dlg.ShowModal() 1417 dlg.Destroy() 1418 1419 if use_existing == wx.ID_CANCEL: 1420 return 1421 1422 # user wants to create new issue with alternate name 1423 if use_existing == wx.ID_NO: 1424 # loop until name modified but non-empty or cancelled 1425 issue_name = episode['description'] 1426 while issue_name == episode['description']: 1427 dlg = wx.TextEntryDialog ( 1428 parent = parent, 1429 message = _('Enter a short descriptive name for the new health issue:'), 1430 caption = _('Creating a new health issue ...'), 1431 defaultValue = issue_name, 1432 style = wx.OK | wx.CANCEL | wx.CENTRE 1433 ) 1434 decision = dlg.ShowModal() 1435 if decision != wx.ID_OK: 1436 dlg.Destroy() 1437 return 1438 issue_name = dlg.GetValue().strip() 1439 dlg.Destroy() 1440 if issue_name == u'': 1441 issue_name = episode['description'] 1442 1443 issue = emr.add_health_issue(issue_name = issue_name) 1444 created_new_issue = True 1445 1446 # eventually move the episode to the issue 1447 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True): 1448 # user cancelled the move so delete just-created issue 1449 if created_new_issue: 1450 # shouldn't fail as it is completely new 1451 gmEMRStructItems.delete_health_issue(health_issue = issue) 1452 return 1453 1454 return
1455 #----------------------------------------------------------------
1456 -def move_episode_to_issue(episode=None, target_issue=None, save_to_backend=False):
1457 """Prepare changing health issue for an episode. 1458 1459 Checks for two-open-episodes conflict. When this 1460 function succeeds, the pk_health_issue has been set 1461 on the episode instance and the episode should - for 1462 all practical purposes - be ready for save_payload(). 1463 """ 1464 # episode is closed: should always work 1465 if not episode['episode_open']: 1466 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1467 if save_to_backend: 1468 episode.save_payload() 1469 return True 1470 1471 # un-associate: should always work, too 1472 if target_issue is None: 1473 episode['pk_health_issue'] = None 1474 if save_to_backend: 1475 episode.save_payload() 1476 return True 1477 1478 # try closing possibly expired episode on target issue if any 1479 db_cfg = gmCfg.cCfgSQL() 1480 epi_ttl = int(db_cfg.get2 ( 1481 option = u'episode.ttl', 1482 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1483 bias = 'user', 1484 default = 60 # 2 months 1485 )) 1486 if target_issue.close_expired_episode(ttl=epi_ttl) is True: 1487 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description'])) 1488 existing_epi = target_issue.get_open_episode() 1489 1490 # no more open episode on target issue: should work now 1491 if existing_epi is None: 1492 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1493 if save_to_backend: 1494 episode.save_payload() 1495 return True 1496 1497 # don't conflict on SELF ;-) 1498 if existing_epi['pk_episode'] == episode['pk_episode']: 1499 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1500 if save_to_backend: 1501 episode.save_payload() 1502 return True 1503 1504 # we got two open episodes at once, ask user 1505 move_range = episode.get_access_range() 1506 exist_range = existing_epi.get_access_range() 1507 question = _( 1508 'You want to associate the running episode:\n\n' 1509 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n' 1510 'with the health issue:\n\n' 1511 ' "%(issue_name)s"\n\n' 1512 'There already is another episode running\n' 1513 'for this health issue:\n\n' 1514 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n' 1515 'However, there can only be one running\n' 1516 'episode per health issue.\n\n' 1517 'Which episode do you want to close ?' 1518 ) % { 1519 'new_epi_name': episode['description'], 1520 'new_epi_start': move_range[0].strftime('%m/%y'), 1521 'new_epi_end': move_range[1].strftime('%m/%y'), 1522 'issue_name': target_issue['description'], 1523 'old_epi_name': existing_epi['description'], 1524 'old_epi_start': exist_range[0].strftime('%m/%y'), 1525 'old_epi_end': exist_range[1].strftime('%m/%y') 1526 } 1527 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1528 parent = None, 1529 id = -1, 1530 caption = _('Resolving two-running-episodes conflict'), 1531 question = question, 1532 button_defs = [ 1533 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']}, 1534 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']} 1535 ] 1536 ) 1537 decision = dlg.ShowModal() 1538 1539 if decision == wx.ID_CANCEL: 1540 # button 3: move cancelled by user 1541 return False 1542 1543 elif decision == wx.ID_YES: 1544 # button 1: close old episode 1545 existing_epi['episode_open'] = False 1546 existing_epi.save_payload() 1547 1548 elif decision == wx.ID_NO: 1549 # button 2: close new episode 1550 episode['episode_open'] = False 1551 1552 else: 1553 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision) 1554 1555 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1556 if save_to_backend: 1557 episode.save_payload() 1558 return True
1559 #----------------------------------------------------------------
1560 -class cEpisodeListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1561 1562 # FIXME: support pre-selection 1563
1564 - def __init__(self, *args, **kwargs):
1565 1566 episodes = kwargs['episodes'] 1567 del kwargs['episodes'] 1568 1569 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1570 1571 self.SetTitle(_('Select the episodes you are interested in ...')) 1572 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')]) 1573 self._LCTRL_items.set_string_items ( 1574 items = [ 1575 [ epi['description'], 1576 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''), 1577 gmTools.coalesce(epi['health_issue'], u'') 1578 ] 1579 for epi in episodes ] 1580 ) 1581 self._LCTRL_items.set_column_widths() 1582 self._LCTRL_items.set_data(data = episodes)
1583 #----------------------------------------------------------------
1584 -class cEpisodeDescriptionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1585 """Let user select an episode *description*. 1586 1587 The user can select an episode description from the previously 1588 used descriptions across all episodes across all patients. 1589 1590 Selection is done with a phrasewheel so the user can 1591 type the episode name and matches will be shown. Typing 1592 "*" will show the entire list of episodes. 1593 1594 If the user types a description not existing yet a 1595 new episode description will be returned. 1596 """
1597 - def __init__(self, *args, **kwargs):
1598 1599 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1600 queries = [ 1601 u""" 1602 SELECT DISTINCT ON (description) 1603 description 1604 AS data, 1605 description 1606 AS field_label, 1607 description || ' (' 1608 || CASE 1609 WHEN is_open IS TRUE THEN _('ongoing') 1610 ELSE _('closed') 1611 END 1612 || ')' 1613 AS list_label 1614 FROM 1615 clin.episode 1616 WHERE 1617 description %(fragment_condition)s 1618 ORDER BY description 1619 LIMIT 30 1620 """ 1621 ] 1622 ) 1623 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1624 self.matcher = mp
1625 #----------------------------------------------------------------
1626 -class cEpisodeSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1627 """Let user select an episode. 1628 1629 The user can select an episode from the existing episodes of a 1630 patient. Selection is done with a phrasewheel so the user 1631 can type the episode name and matches will be shown. Typing 1632 "*" will show the entire list of episodes. Closed episodes 1633 will be marked as such. If the user types an episode name not 1634 in the list of existing episodes a new episode can be created 1635 from it if the programmer activated that feature. 1636 1637 If keyword <patient_id> is set to None or left out the control 1638 will listen to patient change signals and therefore act on 1639 gmPerson.gmCurrentPatient() changes. 1640 """
1641 - def __init__(self, *args, **kwargs):
1642 1643 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}} 1644 1645 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1646 queries = [ 1647 u"""( 1648 1649 select 1650 pk_episode 1651 as data, 1652 description 1653 as field_label, 1654 coalesce ( 1655 description || ' - ' || health_issue, 1656 description 1657 ) as list_label, 1658 1 as rank 1659 from 1660 clin.v_pat_episodes 1661 where 1662 episode_open is true and 1663 description %(fragment_condition)s 1664 %(ctxt_pat)s 1665 1666 ) union all ( 1667 1668 select 1669 pk_episode 1670 as data, 1671 description 1672 as field_label, 1673 coalesce ( 1674 description || _(' (closed)') || ' - ' || health_issue, 1675 description || _(' (closed)') 1676 ) as list_label, 1677 2 as rank 1678 from 1679 clin.v_pat_episodes 1680 where 1681 description %(fragment_condition)s and 1682 episode_open is false 1683 %(ctxt_pat)s 1684 1685 ) 1686 1687 order by rank, list_label 1688 limit 30""" 1689 ], 1690 context = ctxt 1691 ) 1692 1693 try: 1694 kwargs['patient_id'] 1695 except KeyError: 1696 kwargs['patient_id'] = None 1697 1698 if kwargs['patient_id'] is None: 1699 self.use_current_patient = True 1700 self.__register_patient_change_signals() 1701 pat = gmPerson.gmCurrentPatient() 1702 if pat.connected: 1703 mp.set_context('pat', pat.ID) 1704 else: 1705 self.use_current_patient = False 1706 self.__patient_id = int(kwargs['patient_id']) 1707 mp.set_context('pat', self.__patient_id) 1708 1709 del kwargs['patient_id'] 1710 1711 gmPhraseWheel.cPhraseWheel.__init__ ( 1712 self, 1713 *args, 1714 **kwargs 1715 ) 1716 self.matcher = mp
1717 #-------------------------------------------------------- 1718 # external API 1719 #--------------------------------------------------------
1720 - def set_patient(self, patient_id=None):
1721 if self.use_current_patient: 1722 return False 1723 self.__patient_id = int(patient_id) 1724 self.set_context('pat', self.__patient_id) 1725 return True
1726 #--------------------------------------------------------
1727 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1728 self.__is_open_for_create_data = is_open # used (only) in _create_data() 1729 return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1730 #--------------------------------------------------------
1731 - def _create_data(self):
1732 1733 epi_name = self.GetValue().strip() 1734 if epi_name == u'': 1735 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True) 1736 _log.debug('cannot create episode without name') 1737 return 1738 1739 if self.use_current_patient: 1740 pat = gmPerson.gmCurrentPatient() 1741 else: 1742 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 1743 1744 emr = pat.get_emr() 1745 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data) 1746 if epi is None: 1747 self.data = {} 1748 else: 1749 self.SetText ( 1750 value = epi_name, 1751 data = epi['pk_episode'] 1752 )
1753 #--------------------------------------------------------
1754 - def _data2instance(self):
1755 return gmEMRStructItems.cEpisode(aPK_obj = self.GetData())
1756 #-------------------------------------------------------- 1757 # internal API 1758 #--------------------------------------------------------
1760 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1761 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1762 #--------------------------------------------------------
1763 - def _pre_patient_selection(self):
1764 return True
1765 #--------------------------------------------------------
1766 - def _post_patient_selection(self):
1767 if self.use_current_patient: 1768 patient = gmPerson.gmCurrentPatient() 1769 self.set_context('pat', patient.ID) 1770 return True
1771 #---------------------------------------------------------------- 1772 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl 1773
1774 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1775
1776 - def __init__(self, *args, **kwargs):
1777 1778 try: 1779 episode = kwargs['episode'] 1780 del kwargs['episode'] 1781 except KeyError: 1782 episode = None 1783 1784 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs) 1785 gmEditArea.cGenericEditAreaMixin.__init__(self) 1786 1787 self.data = episode
1788 #---------------------------------------------------------------- 1789 # generic Edit Area mixin API 1790 #----------------------------------------------------------------
1791 - def _valid_for_save(self):
1792 1793 errors = False 1794 1795 if len(self._PRW_description.GetValue().strip()) == 0: 1796 errors = True 1797 self._PRW_description.display_as_valid(False) 1798 self._PRW_description.SetFocus() 1799 else: 1800 self._PRW_description.display_as_valid(True) 1801 self._PRW_description.Refresh() 1802 1803 return not errors
1804 #----------------------------------------------------------------
1805 - def _save_as_new(self):
1806 1807 pat = gmPerson.gmCurrentPatient() 1808 emr = pat.get_emr() 1809 1810 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip()) 1811 epi['summary'] = self._TCTRL_status.GetValue().strip() 1812 epi['episode_open'] = not self._CHBOX_closed.IsChecked() 1813 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 1814 1815 issue_name = self._PRW_issue.GetValue().strip() 1816 if len(issue_name) != 0: 1817 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1818 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue']) 1819 1820 if not move_episode_to_issue(episode = epi, 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 epi['description'], 1825 issue['description'] 1826 ) 1827 ) 1828 gmEMRStructItems.delete_episode(episode = epi) 1829 return False 1830 1831 epi.save() 1832 1833 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 1834 1835 self.data = epi 1836 return True
1837 #----------------------------------------------------------------
1838 - def _save_as_update(self):
1839 1840 self.data['description'] = self._PRW_description.GetValue().strip() 1841 self.data['summary'] = self._TCTRL_status.GetValue().strip() 1842 self.data['episode_open'] = not self._CHBOX_closed.IsChecked() 1843 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 1844 1845 issue_name = self._PRW_issue.GetValue().strip() 1846 if len(issue_name) == 0: 1847 self.data['pk_health_issue'] = None 1848 else: 1849 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1850 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue']) 1851 1852 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False): 1853 gmDispatcher.send ( 1854 signal = 'statustext', 1855 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1856 self.data['description'], 1857 issue['description'] 1858 ) 1859 ) 1860 return False 1861 1862 self.data.save() 1863 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 1864 1865 return True
1866 #----------------------------------------------------------------
1867 - def _refresh_as_new(self):
1868 if self.data is None: 1869 ident = gmPerson.gmCurrentPatient() 1870 else: 1871 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1872 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1873 self._PRW_issue.SetText() 1874 self._PRW_description.SetText() 1875 self._TCTRL_status.SetValue(u'') 1876 self._PRW_certainty.SetText() 1877 self._CHBOX_closed.SetValue(False) 1878 self._PRW_codes.SetText()
1879 #----------------------------------------------------------------
1880 - def _refresh_from_existing(self):
1881 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1882 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1883 1884 if self.data['pk_health_issue'] is not None: 1885 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue']) 1886 1887 self._PRW_description.SetText(self.data['description'], data=self.data['description']) 1888 1889 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u'')) 1890 1891 if self.data['diagnostic_certainty_classification'] is not None: 1892 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification']) 1893 1894 self._CHBOX_closed.SetValue(not self.data['episode_open']) 1895 1896 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 1897 self._PRW_codes.SetText(val, data)
1898 #----------------------------------------------------------------
1900 self._refresh_as_new()
1901 #================================================================ 1902 # health issue related widgets/functions 1903 #----------------------------------------------------------------
1904 -def edit_health_issue(parent=None, issue=None):
1905 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1) 1906 ea.data = issue 1907 ea.mode = gmTools.coalesce(issue, 'new', 'edit') 1908 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None)) 1909 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue'))) 1910 if dlg.ShowModal() == wx.ID_OK: 1911 return True 1912 return False
1913 #----------------------------------------------------------------
1914 -def select_health_issues(parent=None, emr=None):
1915 1916 if parent is None: 1917 parent = wx.GetApp().GetTopWindow() 1918 #----------------------------------------- 1919 def refresh(lctrl): 1920 issues = emr.get_health_issues() 1921 items = [ 1922 [ 1923 gmTools.bool2subst(i['is_confidential'], _('CONFIDENTIAL'), u'', u''), 1924 i['description'], 1925 gmTools.bool2subst(i['clinically_relevant'], _('relevant'), u'', u''), 1926 gmTools.bool2subst(i['is_active'], _('active'), u'', u''), 1927 gmTools.bool2subst(i['is_cause_of_death'], _('fatal'), u'', u'') 1928 ] for i in issues 1929 ] 1930 lctrl.set_string_items(items = items) 1931 lctrl.set_data(data = issues)
1932 #----------------------------------------- 1933 return gmListWidgets.get_choices_from_list ( 1934 parent = parent, 1935 msg = _('\nSelect the health issues !\n'), 1936 caption = _('Showing health issues ...'), 1937 columns = [u'', _('Health issue'), u'', u'', u''], 1938 single_selection = False, 1939 #edit_callback = edit, 1940 #new_callback = edit, 1941 #delete_callback = delete, 1942 refresh_callback = refresh 1943 ) 1944 #----------------------------------------------------------------
1945 -class cIssueListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1946 1947 # FIXME: support pre-selection 1948
1949 - def __init__(self, *args, **kwargs):
1950 1951 issues = kwargs['issues'] 1952 del kwargs['issues'] 1953 1954 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1955 1956 self.SetTitle(_('Select the health issues you are interested in ...')) 1957 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u'']) 1958 1959 for issue in issues: 1960 if issue['is_confidential']: 1961 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential')) 1962 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED')) 1963 else: 1964 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'') 1965 1966 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description']) 1967 if issue['clinically_relevant']: 1968 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant')) 1969 if issue['is_active']: 1970 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active')) 1971 if issue['is_cause_of_death']: 1972 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal')) 1973 1974 self._LCTRL_items.set_column_widths() 1975 self._LCTRL_items.set_data(data = issues)
1976 #----------------------------------------------------------------
1977 -class cIssueSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1978 """Let the user select a health issue. 1979 1980 The user can select a health issue from the existing issues 1981 of a patient. Selection is done with a phrasewheel so the user 1982 can type the issue name and matches will be shown. Typing 1983 "*" will show the entire list of issues. Inactive issues 1984 will be marked as such. If the user types an issue name not 1985 in the list of existing issues a new issue can be created 1986 from it if the programmer activated that feature. 1987 1988 If keyword <patient_id> is set to None or left out the control 1989 will listen to patient change signals and therefore act on 1990 gmPerson.gmCurrentPatient() changes. 1991 """
1992 - def __init__(self, *args, **kwargs):
1993 1994 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}} 1995 1996 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1997 # FIXME: consider clin.health_issue.clinically_relevant 1998 queries = [ 1999 u""" 2000 SELECT 2001 data, 2002 field_label, 2003 list_label 2004 FROM (( 2005 SELECT 2006 pk_health_issue AS data, 2007 description AS field_label, 2008 description AS list_label 2009 FROM clin.v_health_issues 2010 WHERE 2011 is_active IS true 2012 AND 2013 description %(fragment_condition)s 2014 AND 2015 %(ctxt_pat)s 2016 2017 ) UNION ( 2018 2019 SELECT 2020 pk_health_issue AS data, 2021 description AS field_label, 2022 description || _(' (inactive)') AS list_label 2023 FROM clin.v_health_issues 2024 WHERE 2025 is_active IS false 2026 AND 2027 description %(fragment_condition)s 2028 AND 2029 %(ctxt_pat)s 2030 )) AS union_query 2031 ORDER BY 2032 list_label"""], 2033 context = ctxt 2034 ) 2035 2036 try: kwargs['patient_id'] 2037 except KeyError: kwargs['patient_id'] = None 2038 2039 if kwargs['patient_id'] is None: 2040 self.use_current_patient = True 2041 self.__register_patient_change_signals() 2042 pat = gmPerson.gmCurrentPatient() 2043 if pat.connected: 2044 mp.set_context('pat', pat.ID) 2045 else: 2046 self.use_current_patient = False 2047 self.__patient_id = int(kwargs['patient_id']) 2048 mp.set_context('pat', self.__patient_id) 2049 2050 del kwargs['patient_id'] 2051 2052 gmPhraseWheel.cPhraseWheel.__init__ ( 2053 self, 2054 *args, 2055 **kwargs 2056 ) 2057 self.matcher = mp
2058 #-------------------------------------------------------- 2059 # external API 2060 #--------------------------------------------------------
2061 - def set_patient(self, patient_id=None):
2062 if self.use_current_patient: 2063 return False 2064 self.__patient_id = int(patient_id) 2065 self.set_context('pat', self.__patient_id) 2066 return True
2067 #--------------------------------------------------------
2068 - def _create_data(self):
2069 issue_name = self.GetValue().strip() 2070 if issue_name == u'': 2071 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create health issue without name.'), beep = True) 2072 _log.debug('cannot create health issue without name') 2073 return 2074 2075 if self.use_current_patient: 2076 pat = gmPerson.gmCurrentPatient() 2077 else: 2078 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 2079 2080 emr = pat.get_emr() 2081 issue = emr.add_health_issue(issue_name = issue_name) 2082 2083 if issue is None: 2084 self.data = {} 2085 else: 2086 self.SetText ( 2087 value = issue_name, 2088 data = issue['pk_health_issue'] 2089 )
2090 #--------------------------------------------------------
2091 - def _data2instance(self):
2092 return gmEMRStructItems.cHealthIssue(aPK_obj = self.GetData())
2093 #-------------------------------------------------------- 2094 # internal API 2095 #--------------------------------------------------------
2097 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 2098 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
2099 #--------------------------------------------------------
2100 - def _pre_patient_selection(self):
2101 return True
2102 #--------------------------------------------------------
2103 - def _post_patient_selection(self):
2104 if self.use_current_patient: 2105 patient = gmPerson.gmCurrentPatient() 2106 self.set_context('pat', patient.ID) 2107 return True
2108 #------------------------------------------------------------ 2109 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg 2110
2111 -class cIssueSelectionDlg(wxgIssueSelectionDlg.wxgIssueSelectionDlg):
2112
2113 - def __init__(self, *args, **kwargs):
2114 try: 2115 msg = kwargs['message'] 2116 except KeyError: 2117 msg = None 2118 del kwargs['message'] 2119 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs) 2120 if msg is not None: 2121 self._lbl_message.SetLabel(label=msg)
2122 #--------------------------------------------------------
2123 - def _on_OK_button_pressed(self, event):
2124 event.Skip() 2125 pk_issue = self._PhWheel_issue.GetData(can_create=True) 2126 if pk_issue is None: 2127 gmGuiHelpers.gm_show_error ( 2128 _('Cannot create new health issue:\n [%(issue)s]') % {'issue': self._PhWheel_issue.GetValue().strip()}, 2129 _('Selecting health issue') 2130 ) 2131 return False 2132 return True
2133 #------------------------------------------------------------ 2134 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl 2135
2136 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
2137 """Panel encapsulating health issue edit area functionality.""" 2138
2139 - def __init__(self, *args, **kwargs):
2140 2141 try: 2142 issue = kwargs['issue'] 2143 except KeyError: 2144 issue = None 2145 2146 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs) 2147 2148 gmEditArea.cGenericEditAreaMixin.__init__(self) 2149 2150 # FIXME: include more sources: coding systems/other database columns 2151 mp = gmMatchProvider.cMatchProvider_SQL2 ( 2152 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"] 2153 ) 2154 mp.setThresholds(1, 3, 5) 2155 self._PRW_condition.matcher = mp 2156 2157 mp = gmMatchProvider.cMatchProvider_SQL2 ( 2158 queries = [u""" 2159 select distinct on (grouping) grouping, grouping from ( 2160 2161 select rank, grouping from (( 2162 2163 select 2164 grouping, 2165 1 as rank 2166 from 2167 clin.health_issue 2168 where 2169 grouping %%(fragment_condition)s 2170 and 2171 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter) 2172 2173 ) union ( 2174 2175 select 2176 grouping, 2177 2 as rank 2178 from 2179 clin.health_issue 2180 where 2181 grouping %%(fragment_condition)s 2182 2183 )) as union_result 2184 2185 order by rank 2186 2187 ) as order_result 2188 2189 limit 50""" % gmPerson.gmCurrentPatient().ID 2190 ] 2191 ) 2192 mp.setThresholds(1, 3, 5) 2193 self._PRW_grouping.matcher = mp 2194 2195 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted) 2196 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted) 2197 2198 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted) 2199 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted) 2200 2201 self._PRW_year_noted.Enable(True) 2202 2203 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes) 2204 2205 self.data = issue
2206 #---------------------------------------------------------------- 2207 # generic Edit Area mixin API 2208 #----------------------------------------------------------------
2209 - def _valid_for_save(self):
2210 2211 if self._PRW_condition.GetValue().strip() == '': 2212 self._PRW_condition.display_as_valid(False) 2213 self._PRW_condition.SetFocus() 2214 return False 2215 self._PRW_condition.display_as_valid(True) 2216 self._PRW_condition.Refresh() 2217 2218 # FIXME: sanity check age/year diagnosed 2219 age_noted = self._PRW_age_noted.GetValue().strip() 2220 if age_noted != '': 2221 if gmDateTime.str2interval(str_interval = age_noted) is None: 2222 self._PRW_age_noted.display_as_valid(False) 2223 self._PRW_age_noted.SetFocus() 2224 return False 2225 self._PRW_age_noted.display_as_valid(True) 2226 2227 return True
2228 #----------------------------------------------------------------
2229 - def _save_as_new(self):
2230 pat = gmPerson.gmCurrentPatient() 2231 emr = pat.get_emr() 2232 2233 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip()) 2234 2235 side = u'' 2236 if self._ChBOX_left.GetValue(): 2237 side += u's' 2238 if self._ChBOX_right.GetValue(): 2239 side += u'd' 2240 issue['laterality'] = side 2241 2242 issue['summary'] = self._TCTRL_status.GetValue().strip() 2243 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 2244 issue['grouping'] = self._PRW_grouping.GetValue().strip() 2245 issue['is_active'] = self._ChBOX_active.GetValue() 2246 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue() 2247 issue['is_confidential'] = self._ChBOX_confidential.GetValue() 2248 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue() 2249 2250 age_noted = self._PRW_age_noted.GetData() 2251 if age_noted is not None: 2252 issue['age_noted'] = age_noted 2253 2254 issue.save() 2255 2256 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2257 2258 self.data = issue 2259 return True
2260 #----------------------------------------------------------------
2261 - def _save_as_update(self):
2262 2263 self.data['description'] = self._PRW_condition.GetValue().strip() 2264 2265 side = u'' 2266 if self._ChBOX_left.GetValue(): 2267 side += u's' 2268 if self._ChBOX_right.GetValue(): 2269 side += u'd' 2270 self.data['laterality'] = side 2271 2272 self.data['summary'] = self._TCTRL_status.GetValue().strip() 2273 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() 2274 self.data['grouping'] = self._PRW_grouping.GetValue().strip() 2275 self.data['is_active'] = bool(self._ChBOX_active.GetValue()) 2276 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue()) 2277 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue()) 2278 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue()) 2279 2280 age_noted = self._PRW_age_noted.GetData() 2281 if age_noted is not None: 2282 self.data['age_noted'] = age_noted 2283 2284 self.data.save() 2285 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2286 2287 return True
2288 #----------------------------------------------------------------
2289 - def _refresh_as_new(self):
2290 self._PRW_condition.SetText() 2291 self._ChBOX_left.SetValue(0) 2292 self._ChBOX_right.SetValue(0) 2293 self._PRW_codes.SetText() 2294 self._on_leave_codes() 2295 self._PRW_certainty.SetText() 2296 self._PRW_grouping.SetText() 2297 self._TCTRL_status.SetValue(u'') 2298 self._PRW_age_noted.SetText() 2299 self._PRW_year_noted.SetText() 2300 self._ChBOX_active.SetValue(1) 2301 self._ChBOX_relevant.SetValue(1) 2302 self._ChBOX_confidential.SetValue(0) 2303 self._ChBOX_caused_death.SetValue(0) 2304 2305 return True
2306 #----------------------------------------------------------------
2307 - def _refresh_from_existing(self):
2308 self._PRW_condition.SetText(self.data['description']) 2309 2310 lat = gmTools.coalesce(self.data['laterality'], '') 2311 if lat.find('s') == -1: 2312 self._ChBOX_left.SetValue(0) 2313 else: 2314 self._ChBOX_left.SetValue(1) 2315 if lat.find('d') == -1: 2316 self._ChBOX_right.SetValue(0) 2317 else: 2318 self._ChBOX_right.SetValue(1) 2319 2320 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 2321 self._PRW_codes.SetText(val, data) 2322 self._on_leave_codes() 2323 2324 if self.data['diagnostic_certainty_classification'] is not None: 2325 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification']) 2326 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u'')) 2327 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u'')) 2328 2329 if self.data['age_noted'] is None: 2330 self._PRW_age_noted.SetText() 2331 else: 2332 self._PRW_age_noted.SetText ( 2333 value = '%sd' % self.data['age_noted'].days, 2334 data = self.data['age_noted'] 2335 ) 2336 2337 self._ChBOX_active.SetValue(self.data['is_active']) 2338 self._ChBOX_relevant.SetValue(self.data['clinically_relevant']) 2339 self._ChBOX_confidential.SetValue(self.data['is_confidential']) 2340 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death']) 2341 2342 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ... 2343 # self._PRW_age_noted.SetFocus() 2344 # self._PRW_condition.SetFocus() 2345 2346 return True
2347 #----------------------------------------------------------------
2349 return self._refresh_as_new()
2350 #-------------------------------------------------------- 2351 # internal helpers 2352 #--------------------------------------------------------
2353 - def _on_leave_codes(self, *args, **kwargs):
2354 if not self._PRW_codes.IsModified(): 2355 return True 2356 2357 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2358 #--------------------------------------------------------
2359 - def _on_leave_age_noted(self, *args, **kwargs):
2360 2361 if not self._PRW_age_noted.IsModified(): 2362 return True 2363 2364 str_age = self._PRW_age_noted.GetValue().strip() 2365 2366 if str_age == u'': 2367 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2368 return True 2369 2370 age = gmDateTime.str2interval(str_interval = str_age) 2371 2372 if age is None: 2373 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age) 2374 self._PRW_age_noted.SetBackgroundColour('pink') 2375 self._PRW_age_noted.Refresh() 2376 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2377 return True 2378 2379 pat = gmPerson.gmCurrentPatient() 2380 if pat['dob'] is not None: 2381 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob'] 2382 2383 if age >= max_age: 2384 gmDispatcher.send ( 2385 signal = 'statustext', 2386 msg = _( 2387 'Health issue cannot have been noted at age %s. Patient is only %s old.' 2388 ) % (age, pat.get_medical_age()) 2389 ) 2390 self._PRW_age_noted.SetBackgroundColour('pink') 2391 self._PRW_age_noted.Refresh() 2392 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2393 return True 2394 2395 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2396 self._PRW_age_noted.Refresh() 2397 self._PRW_age_noted.SetData(data=age) 2398 2399 if pat['dob'] is not None: 2400 fts = gmDateTime.cFuzzyTimestamp ( 2401 timestamp = pat['dob'] + age, 2402 accuracy = gmDateTime.acc_months 2403 ) 2404 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts) 2405 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB 2406 #wx.CallAfter(self._ChBOX_active.SetFocus) 2407 # if we do the following instead it will take us to the save/update button ... 2408 #wx.CallAfter(self.Navigate) 2409 2410 return True
2411 #--------------------------------------------------------
2412 - def _on_leave_year_noted(self, *args, **kwargs):
2413 2414 if not self._PRW_year_noted.IsModified(): 2415 return True 2416 2417 year_noted = self._PRW_year_noted.GetData() 2418 2419 if year_noted is None: 2420 if self._PRW_year_noted.GetValue().strip() == u'': 2421 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2422 return True 2423 self._PRW_year_noted.SetBackgroundColour('pink') 2424 self._PRW_year_noted.Refresh() 2425 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2426 return True 2427 2428 year_noted = year_noted.get_pydt() 2429 2430 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo): 2431 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.')) 2432 self._PRW_year_noted.SetBackgroundColour('pink') 2433 self._PRW_year_noted.Refresh() 2434 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2435 return True 2436 2437 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2438 self._PRW_year_noted.Refresh() 2439 2440 pat = gmPerson.gmCurrentPatient() 2441 if pat['dob'] is not None: 2442 issue_age = year_noted - pat['dob'] 2443 str_age = gmDateTime.format_interval_medically(interval = issue_age) 2444 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age) 2445 2446 return True
2447 #--------------------------------------------------------
2448 - def _on_modified_age_noted(self, *args, **kwargs):
2449 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2450 return True
2451 #--------------------------------------------------------
2452 - def _on_modified_year_noted(self, *args, **kwargs):
2453 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2454 return True
2455 #================================================================ 2456 # diagnostic certainty related widgets/functions 2457 #----------------------------------------------------------------
2458 -class cDiagnosticCertaintyClassificationPhraseWheel(gmPhraseWheel.cPhraseWheel):
2459
2460 - def __init__(self, *args, **kwargs):
2461 2462 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2463 2464 self.selection_only = False # can be NULL, too 2465 2466 mp = gmMatchProvider.cMatchProvider_FixedList ( 2467 aSeq = [ 2468 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1}, 2469 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1}, 2470 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1}, 2471 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1} 2472 ] 2473 ) 2474 mp.setThresholds(1, 2, 4) 2475 self.matcher = mp 2476 2477 self.SetToolTipString(_( 2478 "The diagnostic classification or grading of this assessment.\n" 2479 "\n" 2480 "This documents how certain one is about this being a true diagnosis." 2481 ))
2482 #================================================================ 2483 # MAIN 2484 #---------------------------------------------------------------- 2485 if __name__ == '__main__': 2486 2487 from Gnumed.business import gmPersonSearch 2488 from Gnumed.wxpython import gmPatSearchWidgets 2489 2490 #================================================================
2491 - class testapp (wx.App):
2492 """ 2493 Test application for testing EMR struct widgets 2494 """ 2495 #--------------------------------------------------------
2496 - def OnInit (self):
2497 """ 2498 Create test application UI 2499 """ 2500 frame = wx.Frame ( 2501 None, 2502 -4, 2503 'Testing EMR struct widgets', 2504 size=wx.Size(600, 400), 2505 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 2506 ) 2507 filemenu= wx.Menu() 2508 filemenu.AppendSeparator() 2509 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application") 2510 2511 # Creating the menubar. 2512 menuBar = wx.MenuBar() 2513 menuBar.Append(filemenu,"&File") 2514 2515 frame.SetMenuBar(menuBar) 2516 2517 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"), 2518 wx.DefaultPosition, wx.DefaultSize, 0 ) 2519 2520 # event handlers 2521 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow) 2522 2523 # patient EMR 2524 self.__pat = gmPerson.gmCurrentPatient() 2525 2526 frame.Show(1) 2527 return 1
2528 #--------------------------------------------------------
2529 - def OnCloseWindow (self, e):
2530 """ 2531 Close test aplication 2532 """ 2533 self.ExitMainLoop ()
2534 #----------------------------------------------------------------
2535 - def test_encounter_edit_area_panel():
2536 app = wx.PyWidgetTester(size = (200, 300)) 2537 emr = pat.get_emr() 2538 enc = emr.active_encounter 2539 #enc = gmEMRStructItems.cEncounter(1) 2540 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc) 2541 app.frame.Show(True) 2542 app.MainLoop() 2543 return
2544 #----------------------------------------------------------------
2545 - def test_encounter_edit_area_dialog():
2546 app = wx.PyWidgetTester(size = (200, 300)) 2547 emr = pat.get_emr() 2548 enc = emr.active_encounter 2549 #enc = gmEMRStructItems.cEncounter(1) 2550 2551 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc) 2552 dlg.ShowModal()
2553 2554 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc) 2555 # app.frame.Show(True) 2556 # app.MainLoop() 2557 #----------------------------------------------------------------
2558 - def test_epsiode_edit_area_pnl():
2559 app = wx.PyWidgetTester(size = (200, 300)) 2560 emr = pat.get_emr() 2561 epi = emr.get_episodes()[0] 2562 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi) 2563 app.frame.Show(True) 2564 app.MainLoop()
2565 #----------------------------------------------------------------
2566 - def test_episode_edit_area_dialog():
2567 app = wx.PyWidgetTester(size = (200, 300)) 2568 emr = pat.get_emr() 2569 epi = emr.get_episodes()[0] 2570 edit_episode(parent=app.frame, episode=epi)
2571 #----------------------------------------------------------------
2572 - def test_hospital_stay_prw():
2573 app = wx.PyWidgetTester(size = (400, 40)) 2574 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2575 app.MainLoop()
2576 #----------------------------------------------------------------
2577 - def test_episode_selection_prw():
2578 app = wx.PyWidgetTester(size = (400, 40)) 2579 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2580 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID) 2581 app.MainLoop()
2582 #----------------------------------------------------------------
2583 - def test_health_issue_edit_area_dlg():
2584 app = wx.PyWidgetTester(size = (200, 300)) 2585 edit_health_issue(parent=app.frame, issue=None)
2586 #----------------------------------------------------------------
2587 - def test_health_issue_edit_area_pnl():
2588 app = wx.PyWidgetTester(size = (200, 300)) 2589 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400)) 2590 app.MainLoop()
2591 #----------------------------------------------------------------
2592 - def test_edit_procedure():
2593 app = wx.PyWidgetTester(size = (200, 300)) 2594 edit_procedure(parent=app.frame)
2595 #================================================================ 2596 2597 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 2598 2599 gmI18N.activate_locale() 2600 gmI18N.install_domain() 2601 gmDateTime.init() 2602 2603 # obtain patient 2604 pat = gmPersonSearch.ask_for_patient() 2605 if pat is None: 2606 print "No patient. Exiting gracefully..." 2607 sys.exit(0) 2608 gmPatSearchWidgets.set_active_patient(patient=pat) 2609 2610 # try: 2611 # lauch emr dialogs test application 2612 # app = testapp(0) 2613 # app.MainLoop() 2614 # except StandardError: 2615 # _log.exception("unhandled exception caught !") 2616 # but re-raise them 2617 # raise 2618 2619 #test_encounter_edit_area_panel() 2620 #test_encounter_edit_area_dialog() 2621 #test_epsiode_edit_area_pnl() 2622 #test_episode_edit_area_dialog() 2623 #test_health_issue_edit_area_dlg() 2624 #test_episode_selection_prw() 2625 #test_hospital_stay_prw() 2626 test_edit_procedure() 2627 2628 #================================================================ 2629