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