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