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''), 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 if not self._DP_admission.GetValue().IsValid(): 554 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 555 wxps.Publisher().sendMessage ( 556 topic = 'statustext', 557 data = {'msg': _('Missing admission data. Cannot save hospital stay.'), 'beep': True} 558 ) 559 return False 560 else: 561 self._DP_admission.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 562 563 if self._DP_discharge.GetValue().IsValid(): 564 if not self._DP_discharge.GetValue().IsLaterThan(self._DP_admission.GetValue()): 565 self._DP_discharge.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 566 wxps.Publisher().sendMessage ( 567 topic = 'statustext', 568 data = {'msg': _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), 'beep': True} 569 ) 570 return False 571 572 if self._PRW_episode.GetValue().strip() == u'': 573 self._PRW_episode.display_as_valid(False) 574 wxps.Publisher().sendMessage ( 575 topic = 'statustext', 576 data = {'msg': _('Must select an episode or enter a name for a new one. Cannot save hospital stay.'), 'beep': True} 577 ) 578 return False 579 580 return True
581 #----------------------------------------------------------------
582 - def _save_as_new(self):
583 584 pat = gmPerson.gmCurrentPatient() 585 emr = pat.get_emr() 586 stay = emr.add_hospital_stay(episode = self._PRW_episode.GetData(can_create = True)) 587 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'') 588 stay['admission'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue()) 589 if self._DP_discharge.GetValue().IsValid(): 590 stay['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue()) 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'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_admission.GetValue()) 601 if self._DP_discharge.GetValue().IsValid(): 602 self.data['discharge'] = gmDateTime.wxDate2py_dt(wxDate = self._DP_discharge.GetValue()) 603 self.data.save_payload() 604 605 return True
606 #----------------------------------------------------------------
607 - def _refresh_as_new(self):
608 self._PRW_hospital.SetText(value = u'') 609 self._PRW_episode.SetText(value = u'') 610 self._DP_admission.SetValue(dt = wx.DateTime.UNow())
611 #self._DP_discharge.SetValue(dt = None) 612 #----------------------------------------------------------------
613 - def _refresh_from_existing(self):
614 if self.data['hospital'] is not None: 615 self._PRW_hospital.SetText(value = self.data['hospital']) 616 617 if self.data['pk_episode'] is not None: 618 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode']) 619 620 self._DP_admission.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['admission'], wx = wx)) 621 622 if self.data['discharge'] is not None: 623 self._DP_discharge.SetValue(gmDateTime.py_dt2wxDate(py_dt = self.data['discharge'], wx = wx))
624 #----------------------------------------------------------------
626 print "this was not expected to be used in this edit area"
627 #================================================================ 628 # encounter related widgets/functions 629 #----------------------------------------------------------------
630 -def start_new_encounter(emr=None):
631 emr.start_new_encounter() 632 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True) 633 time.sleep(0.5) 634 gmGuiHelpers.gm_show_info ( 635 _('\nA new encounter was started for the active patient.\n'), 636 _('Start of new encounter') 637 )
638 #---------------------------------------------------------------- 639 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg 640
641 -def edit_encounter(parent=None, encounter=None):
642 if parent is None: 643 parent = wx.GetApp().GetTopWindow() 644 645 # FIXME: use generic dialog 2 646 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter) 647 if dlg.ShowModal() == wx.ID_OK: 648 dlg.Destroy() 649 return True 650 dlg.Destroy() 651 return False
652 #----------------------------------------------------------------
653 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
654 655 if patient is None: 656 patient = gmPerson.gmCurrentPatient() 657 658 if not patient.connected: 659 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 660 return False 661 662 if parent is None: 663 parent = wx.GetApp().GetTopWindow() 664 665 emr = patient.get_emr() 666 667 #-------------------- 668 def refresh(lctrl): 669 if encounters is None: 670 encs = emr.get_encounters() 671 else: 672 encs = encounters 673 674 items = [ 675 [ 676 e['started'].strftime('%x %H:%M'), 677 e['last_affirmed'].strftime('%H:%M'), 678 e['l10n_type'], 679 gmTools.coalesce(e['reason_for_encounter'], u''), 680 gmTools.coalesce(e['assessment_of_encounter'], u''), 681 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin), 682 e['pk_encounter'] 683 ] for e in encs 684 ] 685 686 lctrl.set_string_items(items = items) 687 lctrl.set_data(data = encs)
688 #-------------------- 689 def new(): 690 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID) 691 return edit_encounter(parent = parent, encounter = enc) 692 #-------------------- 693 def edit(enc=None): 694 return edit_encounter(parent = parent, encounter = enc) 695 #-------------------- 696 return gmListWidgets.get_choices_from_list ( 697 parent = parent, 698 msg = _('\nBelow find the relevant encounters of the patient.\n'), 699 caption = _('Encounters ...'), 700 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'], 701 can_return_empty = True, 702 single_selection = single_selection, 703 refresh_callback = refresh, 704 edit_callback = edit, 705 new_callback = new, 706 ignore_OK_button = ignore_OK_button 707 ) 708 #----------------------------------------------------------------
709 -def ask_for_encounter_continuation(msg=None, caption=None, encounter=None, parent=None):
710 """This is used as the callback when the EMR detects that the 711 patient was here rather recently and wants to ask the 712 provider whether to continue the recent encounter. 713 """ 714 if parent is None: 715 parent = wx.GetApp().GetTopWindow() 716 717 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 718 parent = None, 719 id = -1, 720 caption = caption, 721 question = msg, 722 button_defs = [ 723 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False}, 724 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True} 725 ], 726 show_checkbox = False 727 ) 728 729 result = dlg.ShowModal() 730 dlg.Destroy() 731 732 if result == wx.ID_YES: 733 return True 734 735 return False
736 #----------------------------------------------------------------
737 -def manage_encounter_types(parent=None):
738 739 if parent is None: 740 parent = wx.GetApp().GetTopWindow() 741 742 #-------------------- 743 def edit(enc_type=None): 744 return edit_encounter_type(parent = parent, encounter_type = enc_type)
745 #-------------------- 746 def delete(enc_type=None): 747 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']): 748 return True 749 gmDispatcher.send ( 750 signal = u'statustext', 751 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'], 752 beep = True 753 ) 754 return False 755 #-------------------- 756 def refresh(lctrl): 757 enc_types = gmEMRStructItems.get_encounter_types() 758 lctrl.set_string_items(items = enc_types) 759 #-------------------- 760 gmListWidgets.get_choices_from_list ( 761 parent = parent, 762 msg = _('\nSelect the encounter type you want to edit !\n'), 763 caption = _('Managing encounter types ...'), 764 columns = [_('Local name'), _('Encounter type')], 765 single_selection = True, 766 edit_callback = edit, 767 new_callback = edit, 768 delete_callback = delete, 769 refresh_callback = refresh 770 ) 771 #----------------------------------------------------------------
772 -def edit_encounter_type(parent=None, encounter_type=None):
773 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1) 774 ea.data = encounter_type 775 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit') 776 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea) 777 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name'))) 778 if dlg.ShowModal() == wx.ID_OK: 779 return True 780 return False
781 #----------------------------------------------------------------
782 -class cEncounterTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
783 """Phrasewheel to allow selection of encounter type. 784 785 - user input interpreted as encounter type in English or local language 786 - data returned is pk of corresponding encounter type or None 787 """
788 - def __init__(self, *args, **kwargs):
789 790 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs) 791 792 mp = gmMatchProvider.cMatchProvider_SQL2 ( 793 queries = [ 794 u""" 795 select pk, l10n_description from ( 796 select distinct on (pk) * from ( 797 (select 798 pk, 799 _(description) as l10n_description, 800 1 as rank 801 from 802 clin.encounter_type 803 where 804 _(description) %(fragment_condition)s 805 806 ) union all ( 807 808 select 809 pk, 810 _(description) as l10n_description, 811 2 as rank 812 from 813 clin.encounter_type 814 where 815 description %(fragment_condition)s 816 ) 817 ) as q_distinct_pk 818 ) as q_ordered order by rank, l10n_description 819 """ ] 820 ) 821 mp.setThresholds(2, 4, 6) 822 823 self.matcher = mp 824 self.selection_only = True 825 self.picklist_delay = 50
826 #----------------------------------------------------------------
827 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
828
829 - def __init__(self, *args, **kwargs):
833 834 # self.__register_interests() 835 #------------------------------------------------------- 836 # generic edit area API 837 #-------------------------------------------------------
838 - def _valid_for_save(self):
839 if self.mode == 'edit': 840 if self._TCTRL_l10n_name.GetValue().strip() == u'': 841 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 842 return False 843 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 844 return True 845 846 no_errors = True 847 848 if self._TCTRL_l10n_name.GetValue().strip() == u'': 849 if self._TCTRL_name.GetValue().strip() == u'': 850 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False) 851 no_errors = False 852 else: 853 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 854 else: 855 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True) 856 857 if self._TCTRL_name.GetValue().strip() == u'': 858 if self._TCTRL_l10n_name.GetValue().strip() == u'': 859 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False) 860 no_errors = False 861 else: 862 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 863 else: 864 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True) 865 866 return no_errors
867 #-------------------------------------------------------
868 - def _save_as_new(self):
869 enc_type = gmEMRStructItems.create_encounter_type ( 870 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''), 871 l10n_description = gmTools.coalesce ( 872 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''), 873 self._TCTRL_name.GetValue().strip() 874 ) 875 ) 876 if enc_type is None: 877 return False 878 self.data = enc_type 879 return True
880 #-------------------------------------------------------
881 - def _save_as_update(self):
882 enc_type = gmEMRStructItems.update_encounter_type ( 883 description = self._TCTRL_name.GetValue().strip(), 884 l10n_description = self._TCTRL_l10n_name.GetValue().strip() 885 ) 886 if enc_type is None: 887 return False 888 self.data = enc_type 889 return True
890 #-------------------------------------------------------
891 - def _refresh_as_new(self):
892 self._TCTRL_l10n_name.SetValue(u'') 893 self._TCTRL_name.SetValue(u'') 894 self._TCTRL_name.Enable(True)
895 #-------------------------------------------------------
896 - def _refresh_from_existing(self):
897 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 898 self._TCTRL_name.SetValue(self.data['description']) 899 # disallow changing type on all encounters by editing system name 900 self._TCTRL_name.Enable(False)
901 #-------------------------------------------------------
903 self._TCTRL_l10n_name.SetValue(self.data['l10n_description']) 904 self._TCTRL_name.SetValue(self.data['description']) 905 self._TCTRL_name.Enable(True)
906 #------------------------------------------------------- 907 # internal API 908 #------------------------------------------------------- 909 # def __register_interests(self): 910 # return 911 #---------------------------------------------------------------- 912 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl 913
914 -class cEncounterEditAreaPnl(wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl):
915
916 - def __init__(self, *args, **kwargs):
917 try: 918 self.__encounter = kwargs['encounter'] 919 del kwargs['encounter'] 920 except KeyError: 921 self.__encounter = None 922 923 try: 924 msg = kwargs['msg'] 925 del kwargs['msg'] 926 except KeyError: 927 msg = None 928 929 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs) 930 931 self.refresh(msg = msg)
932 #-------------------------------------------------------- 933 # external API 934 #--------------------------------------------------------
935 - def refresh(self, encounter=None, msg=None):
936 937 if msg is not None: 938 self._LBL_instructions.SetLabel(msg) 939 940 if encounter is not None: 941 self.__encounter = encounter 942 943 if self.__encounter is None: 944 return True 945 946 # getting the patient via the encounter allows us to act 947 # on any encounter regardless of the currently active patient 948 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient']) 949 self._LBL_patient.SetLabel(pat.get_description_gender()) 950 951 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type']) 952 953 fts = gmDateTime.cFuzzyTimestamp ( 954 timestamp = self.__encounter['started'], 955 accuracy = gmDateTime.acc_minutes 956 ) 957 self._PRW_start.SetText(fts.format_accurately(), data=fts) 958 959 fts = gmDateTime.cFuzzyTimestamp ( 960 timestamp = self.__encounter['last_affirmed'], 961 accuracy = gmDateTime.acc_minutes 962 ) 963 self._PRW_end.SetText(fts.format_accurately(), data=fts) 964 965 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], '')) 966 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], '')) 967 968 if self.__encounter['last_affirmed'] == self.__encounter['started']: 969 self._PRW_end.SetFocus() 970 else: 971 self._TCTRL_aoe.SetFocus() 972 973 return True
974 #--------------------------------------------------------
975 - def __is_valid_for_save(self):
976 977 if self._PRW_encounter_type.GetData() is None: 978 self._PRW_encounter_type.SetBackgroundColour('pink') 979 self._PRW_encounter_type.Refresh() 980 self._PRW_encounter_type.SetFocus() 981 return False 982 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 983 self._PRW_encounter_type.Refresh() 984 985 if (not self._PRW_start.is_valid_timestamp()) or (self._PRW_start.GetValue().strip() == u''): 986 self._PRW_start.SetFocus() 987 return False 988 989 if (not self._PRW_end.is_valid_timestamp()) or (self._PRW_end.GetValue().strip() == u''): 990 self._PRW_end.SetFocus() 991 return False 992 993 return True
994 #--------------------------------------------------------
995 - def save(self):
996 if not self.__is_valid_for_save(): 997 return False 998 999 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData() 1000 self.__encounter['started'] = self._PRW_start.GetData().get_pydt() 1001 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt() 1002 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'') 1003 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'') 1004 self.__encounter.save_payload() # FIXME: error checking 1005 1006 return True
1007 #---------------------------------------------------------------- 1008 # FIXME: use generic dialog 2
1009 -class cEncounterEditAreaDlg(wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg):
1010
1011 - def __init__(self, *args, **kwargs):
1012 encounter = kwargs['encounter'] 1013 del kwargs['encounter'] 1014 1015 try: 1016 button_defs = kwargs['button_defs'] 1017 del kwargs['button_defs'] 1018 except KeyError: 1019 button_defs = None 1020 1021 try: 1022 msg = kwargs['msg'] 1023 del kwargs['msg'] 1024 except KeyError: 1025 msg = None 1026 1027 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs) 1028 self.SetSize((450, 280)) 1029 self.SetMinSize((450, 280)) 1030 1031 if button_defs is not None: 1032 self._BTN_save.SetLabel(button_defs[0][0]) 1033 self._BTN_save.SetToolTipString(button_defs[0][1]) 1034 self._BTN_close.SetLabel(button_defs[1][0]) 1035 self._BTN_close.SetToolTipString(button_defs[1][1]) 1036 self.Refresh() 1037 1038 self._PNL_edit_area.refresh(encounter = encounter, msg = msg) 1039 1040 self.Fit()
1041 #--------------------------------------------------------
1042 - def _on_save_button_pressed(self, evt):
1043 if self._PNL_edit_area.save(): 1044 if self.IsModal(): 1045 self.EndModal(wx.ID_OK) 1046 else: 1047 self.Close()
1048 #================================================================ 1049 # episode related widgets/functions 1050 #----------------------------------------------------------------
1051 -def edit_episode(parent=None, episode=None):
1052 ea = cEpisodeEditAreaPnl(parent = parent, id = -1) 1053 ea.data = episode 1054 ea.mode = gmTools.coalesce(episode, 'new', 'edit') 1055 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1056 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode'))) 1057 if dlg.ShowModal() == wx.ID_OK: 1058 return True 1059 return False
1060 #----------------------------------------------------------------
1061 -def promote_episode_to_issue(parent=None, episode=None, emr=None):
1062 1063 created_new_issue = False 1064 1065 try: 1066 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient']) 1067 except gmExceptions.NoSuchBusinessObjectError: 1068 issue = None 1069 1070 if issue is None: 1071 issue = emr.add_health_issue(issue_name = episode['description']) 1072 created_new_issue = True 1073 else: 1074 # issue exists already, so ask user 1075 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1076 parent, 1077 -1, 1078 caption = _('Promoting episode to health issue'), 1079 question = _( 1080 'There already is a health issue\n' 1081 '\n' 1082 ' %s\n' 1083 '\n' 1084 'What do you want to do ?' 1085 ) % issue['description'], 1086 button_defs = [ 1087 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False}, 1088 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True} 1089 ] 1090 ) 1091 use_existing = dlg.ShowModal() 1092 dlg.Destroy() 1093 1094 if use_existing == wx.ID_CANCEL: 1095 return 1096 1097 # user wants to create new issue with alternate name 1098 if use_existing == wx.ID_NO: 1099 # loop until name modified but non-empty or cancelled 1100 issue_name = episode['description'] 1101 while issue_name == episode['description']: 1102 dlg = wx.TextEntryDialog ( 1103 parent = parent, 1104 message = _('Enter a short descriptive name for the new health issue:'), 1105 caption = _('Creating a new health issue ...'), 1106 defaultValue = issue_name, 1107 style = wx.OK | wx.CANCEL | wx.CENTRE 1108 ) 1109 decision = dlg.ShowModal() 1110 if decision != wx.ID_OK: 1111 dlg.Destroy() 1112 return 1113 issue_name = dlg.GetValue().strip() 1114 dlg.Destroy() 1115 if issue_name == u'': 1116 issue_name = episode['description'] 1117 1118 issue = emr.add_health_issue(issue_name = issue_name) 1119 created_new_issue = True 1120 1121 # eventually move the episode to the issue 1122 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True): 1123 # user cancelled the move so delete just-created issue 1124 if created_new_issue: 1125 # shouldn't fail as it is completely new 1126 gmEMRStructItems.delete_health_issue(health_issue = issue) 1127 return 1128 1129 return
1130 #----------------------------------------------------------------
1131 -def move_episode_to_issue(episode=None, target_issue=None, save_to_backend=False):
1132 """Prepare changing health issue for an episode. 1133 1134 Checks for two-open-episodes conflict. When this 1135 function succeeds, the pk_health_issue has been set 1136 on the episode instance and the episode should - for 1137 all practical purposes - be ready for save_payload(). 1138 """ 1139 # episode is closed: should always work 1140 if not episode['episode_open']: 1141 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1142 if save_to_backend: 1143 episode.save_payload() 1144 return True 1145 1146 # un-associate: should always work, too 1147 if target_issue is None: 1148 episode['pk_health_issue'] = None 1149 if save_to_backend: 1150 episode.save_payload() 1151 return True 1152 1153 # try closing possibly expired episode on target issue if any 1154 db_cfg = gmCfg.cCfgSQL() 1155 epi_ttl = int(db_cfg.get2 ( 1156 option = u'episode.ttl', 1157 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1158 bias = 'user', 1159 default = 60 # 2 months 1160 )) 1161 if target_issue.close_expired_episode(ttl=epi_ttl) is True: 1162 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description'])) 1163 existing_epi = target_issue.get_open_episode() 1164 1165 # no more open episode on target issue: should work now 1166 if existing_epi is None: 1167 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1168 if save_to_backend: 1169 episode.save_payload() 1170 return True 1171 1172 # don't conflict on SELF ;-) 1173 if existing_epi['pk_episode'] == episode['pk_episode']: 1174 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1175 if save_to_backend: 1176 episode.save_payload() 1177 return True 1178 1179 # we got two open episodes at once, ask user 1180 move_range = episode.get_access_range() 1181 exist_range = existing_epi.get_access_range() 1182 question = _( 1183 'You want to associate the running episode:\n\n' 1184 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n' 1185 'with the health issue:\n\n' 1186 ' "%(issue_name)s"\n\n' 1187 'There already is another episode running\n' 1188 'for this health issue:\n\n' 1189 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n' 1190 'However, there can only be one running\n' 1191 'episode per health issue.\n\n' 1192 'Which episode do you want to close ?' 1193 ) % { 1194 'new_epi_name': episode['description'], 1195 'new_epi_start': move_range[0].strftime('%m/%y'), 1196 'new_epi_end': move_range[1].strftime('%m/%y'), 1197 'issue_name': target_issue['description'], 1198 'old_epi_name': existing_epi['description'], 1199 'old_epi_start': exist_range[0].strftime('%m/%y'), 1200 'old_epi_end': exist_range[1].strftime('%m/%y') 1201 } 1202 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1203 parent = None, 1204 id = -1, 1205 caption = _('Resolving two-running-episodes conflict'), 1206 question = question, 1207 button_defs = [ 1208 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']}, 1209 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']} 1210 ] 1211 ) 1212 decision = dlg.ShowModal() 1213 1214 if decision == wx.ID_CANCEL: 1215 # button 3: move cancelled by user 1216 return False 1217 1218 elif decision == wx.ID_YES: 1219 # button 1: close old episode 1220 existing_epi['episode_open'] = False 1221 existing_epi.save_payload() 1222 1223 elif decision == wx.ID_NO: 1224 # button 2: close new episode 1225 episode['episode_open'] = False 1226 1227 else: 1228 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision) 1229 1230 episode['pk_health_issue'] = target_issue['pk_health_issue'] 1231 if save_to_backend: 1232 episode.save_payload() 1233 return True
1234 #----------------------------------------------------------------
1235 -class cEpisodeListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1236 1237 # FIXME: support pre-selection 1238
1239 - def __init__(self, *args, **kwargs):
1240 1241 episodes = kwargs['episodes'] 1242 del kwargs['episodes'] 1243 1244 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1245 1246 self.SetTitle(_('Select the episodes you are interested in ...')) 1247 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')]) 1248 self._LCTRL_items.set_string_items ( 1249 items = [ 1250 [ epi['description'], 1251 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''), 1252 gmTools.coalesce(epi['health_issue'], u'') 1253 ] 1254 for epi in episodes ] 1255 ) 1256 self._LCTRL_items.set_column_widths() 1257 self._LCTRL_items.set_data(data = episodes)
1258 #----------------------------------------------------------------
1259 -class cEpisodeDescriptionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1260 """Let user select an episode *description*. 1261 1262 The user can select an episode description from the previously 1263 used descriptions across all episodes across all patients. 1264 1265 Selection is done with a phrasewheel so the user can 1266 type the episode name and matches will be shown. Typing 1267 "*" will show the entire list of episodes. 1268 1269 If the user types a description not existing yet a 1270 new episode description will be returned. 1271 """
1272 - def __init__(self, *args, **kwargs):
1273 1274 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1275 queries = [u""" 1276 select distinct on (description) description, description, 1 1277 from clin.episode 1278 where description %(fragment_condition)s 1279 order by description 1280 limit 30""" 1281 ] 1282 ) 1283 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1284 self.matcher = mp
1285 #----------------------------------------------------------------
1286 -class cEpisodeSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1287 """Let user select an episode. 1288 1289 The user can select an episode from the existing episodes of a 1290 patient. Selection is done with a phrasewheel so the user 1291 can type the episode name and matches will be shown. Typing 1292 "*" will show the entire list of episodes. Closed episodes 1293 will be marked as such. If the user types an episode name not 1294 in the list of existing episodes a new episode can be created 1295 from it if the programmer activated that feature. 1296 1297 If keyword <patient_id> is set to None or left out the control 1298 will listen to patient change signals and therefore act on 1299 gmPerson.gmCurrentPatient() changes. 1300 """
1301 - def __init__(self, *args, **kwargs):
1302 1303 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}} 1304 1305 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1306 queries = [ 1307 u"""( 1308 1309 select 1310 pk_episode, 1311 coalesce ( 1312 description || ' - ' || health_issue, 1313 description 1314 ) as description, 1315 1 as rank 1316 from 1317 clin.v_pat_episodes 1318 where 1319 episode_open is true and 1320 description %(fragment_condition)s 1321 %(ctxt_pat)s 1322 1323 ) union all ( 1324 1325 select 1326 pk_episode, 1327 coalesce ( 1328 description || _(' (closed)') || ' - ' || health_issue, 1329 description || _(' (closed)') 1330 ) as description, 1331 2 as rank 1332 from 1333 clin.v_pat_episodes 1334 where 1335 description %(fragment_condition)s and 1336 episode_open is false 1337 %(ctxt_pat)s 1338 1339 ) 1340 order by rank, description 1341 limit 30""" 1342 ], 1343 context = ctxt 1344 ) 1345 1346 try: 1347 kwargs['patient_id'] 1348 except KeyError: 1349 kwargs['patient_id'] = None 1350 1351 if kwargs['patient_id'] is None: 1352 self.use_current_patient = True 1353 self.__register_patient_change_signals() 1354 pat = gmPerson.gmCurrentPatient() 1355 if pat.connected: 1356 mp.set_context('pat', pat.ID) 1357 else: 1358 self.use_current_patient = False 1359 self.__patient_id = int(kwargs['patient_id']) 1360 mp.set_context('pat', self.__patient_id) 1361 1362 del kwargs['patient_id'] 1363 1364 gmPhraseWheel.cPhraseWheel.__init__ ( 1365 self, 1366 *args, 1367 **kwargs 1368 ) 1369 self.matcher = mp
1370 #-------------------------------------------------------- 1371 # external API 1372 #--------------------------------------------------------
1373 - def set_patient(self, patient_id=None):
1374 if self.use_current_patient: 1375 return False 1376 self.__patient_id = int(patient_id) 1377 self.set_context('pat', self.__patient_id) 1378 return True
1379 #--------------------------------------------------------
1380 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1381 self.__is_open_for_create_data = is_open # used (only) in _create_data() 1382 gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance) 1383 return self.data
1384 #--------------------------------------------------------
1385 - def _create_data(self):
1386 1387 epi_name = self.GetValue().strip() 1388 if epi_name == u'': 1389 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True) 1390 _log.debug('cannot create episode without name') 1391 return 1392 1393 if self.use_current_patient: 1394 pat = gmPerson.gmCurrentPatient() 1395 else: 1396 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 1397 1398 emr = pat.get_emr() 1399 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data) 1400 if epi is None: 1401 self.data = None 1402 else: 1403 self.data = epi['pk_episode']
1404 #--------------------------------------------------------
1405 - def _data2instance(self):
1406 return gmEMRStructItems.cEpisode(aPK_obj = self.data)
1407 #-------------------------------------------------------- 1408 # internal API 1409 #--------------------------------------------------------
1411 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1412 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1413 #--------------------------------------------------------
1414 - def _pre_patient_selection(self):
1415 return True
1416 #--------------------------------------------------------
1417 - def _post_patient_selection(self):
1418 if self.use_current_patient: 1419 patient = gmPerson.gmCurrentPatient() 1420 self.set_context('pat', patient.ID) 1421 return True
1422 #---------------------------------------------------------------- 1423 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl 1424
1425 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1426
1427 - def __init__(self, *args, **kwargs):
1428 1429 try: 1430 episode = kwargs['episode'] 1431 del kwargs['episode'] 1432 except KeyError: 1433 episode = None 1434 1435 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs) 1436 gmEditArea.cGenericEditAreaMixin.__init__(self) 1437 1438 self.data = episode
1439 #---------------------------------------------------------------- 1440 # generic Edit Area mixin API 1441 #----------------------------------------------------------------
1442 - def _valid_for_save(self):
1443 1444 errors = False 1445 1446 if len(self._PRW_description.GetValue().strip()) == 0: 1447 errors = True 1448 self._PRW_description.display_as_valid(False) 1449 self._PRW_description.SetFocus() 1450 else: 1451 self._PRW_description.display_as_valid(True) 1452 self._PRW_description.Refresh() 1453 1454 return not errors
1455 #----------------------------------------------------------------
1456 - def _save_as_new(self):
1457 1458 pat = gmPerson.gmCurrentPatient() 1459 emr = pat.get_emr() 1460 1461 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip()) 1462 epi['summary'] = self._TCTRL_summary.GetValue().strip() 1463 epi['episode_open'] = not self._CHBOX_closed.IsChecked() 1464 epi['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1465 1466 issue_name = self._PRW_issue.GetValue().strip() 1467 if len(issue_name) != 0: 1468 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1469 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue']) 1470 1471 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False): 1472 gmDispatcher.send ( 1473 signal = 'statustext', 1474 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1475 epi['description'], 1476 issue['description'] 1477 ) 1478 ) 1479 gmEMRStructItems.delete_episode(episode = epi) 1480 return False 1481 1482 epi.save() 1483 1484 self.data = epi 1485 return True
1486 #----------------------------------------------------------------
1487 - def _save_as_update(self):
1488 1489 self.data['description'] = self._PRW_description.GetValue().strip() 1490 self.data['summary'] = self._TCTRL_summary.GetValue().strip() 1491 self.data['episode_open'] = not self._CHBOX_closed.IsChecked() 1492 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1493 1494 issue_name = self._PRW_issue.GetValue().strip() 1495 if len(issue_name) != 0: 1496 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1497 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue']) 1498 1499 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False): 1500 gmDispatcher.send ( 1501 signal = 'statustext', 1502 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1503 self.data['description'], 1504 issue['description'] 1505 ) 1506 ) 1507 return False 1508 1509 self.data.save() 1510 return True
1511 #----------------------------------------------------------------
1512 - def _refresh_as_new(self):
1513 if self.data is None: 1514 ident = gmPerson.gmCurrentPatient() 1515 else: 1516 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1517 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1518 self._PRW_issue.SetText() 1519 self._PRW_description.SetText() 1520 self._TCTRL_summary.SetValue(u'') 1521 self._PRW_classification.SetText() 1522 self._CHBOX_closed.SetValue(False)
1523 #----------------------------------------------------------------
1524 - def _refresh_from_existing(self):
1525 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1526 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1527 1528 if self.data['pk_health_issue'] is not None: 1529 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue']) 1530 1531 self._PRW_description.SetText(self.data['description'], data=self.data['description']) 1532 1533 self._TCTRL_summary.SetValue(gmTools.coalesce(self.data['summary'], u'')) 1534 1535 if self.data['diagnostic_certainty_classification'] is not None: 1536 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification']) 1537 1538 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1539 #----------------------------------------------------------------
1541 self._refresh_as_new()
1542 #================================================================ 1543 # health issue related widgets/functions 1544 #----------------------------------------------------------------
1545 -def edit_health_issue(parent=None, issue=None):
1546 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1) 1547 ea.data = issue 1548 ea.mode = gmTools.coalesce(issue, 'new', 'edit') 1549 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None)) 1550 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue'))) 1551 if dlg.ShowModal() == wx.ID_OK: 1552 return True 1553 return False
1554 #----------------------------------------------------------------
1555 -class cIssueListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1556 1557 # FIXME: support pre-selection 1558
1559 - def __init__(self, *args, **kwargs):
1560 1561 issues = kwargs['issues'] 1562 del kwargs['issues'] 1563 1564 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1565 1566 self.SetTitle(_('Select the health issues you are interested in ...')) 1567 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u'']) 1568 1569 for issue in issues: 1570 if issue['is_confidential']: 1571 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential')) 1572 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED')) 1573 else: 1574 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'') 1575 1576 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description']) 1577 if issue['clinically_relevant']: 1578 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant')) 1579 if issue['is_active']: 1580 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active')) 1581 if issue['is_cause_of_death']: 1582 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal')) 1583 1584 self._LCTRL_items.set_column_widths() 1585 self._LCTRL_items.set_data(data = issues)
1586 #----------------------------------------------------------------
1587 -class cIssueSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1588 """Let the user select a health issue. 1589 1590 The user can select a health issue from the existing issues 1591 of a patient. Selection is done with a phrasewheel so the user 1592 can type the issue name and matches will be shown. Typing 1593 "*" will show the entire list of issues. Inactive issues 1594 will be marked as such. If the user types an issue name not 1595 in the list of existing issues a new issue can be created 1596 from it if the programmer activated that feature. 1597 1598 If keyword <patient_id> is set to None or left out the control 1599 will listen to patient change signals and therefore act on 1600 gmPerson.gmCurrentPatient() changes. 1601 """
1602 - def __init__(self, *args, **kwargs):
1603 1604 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}} 1605 1606 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1607 # FIXME: consider clin.health_issue.clinically_relevant 1608 queries = [u""" 1609 (select pk_health_issue, description, 1 1610 from clin.v_health_issues where 1611 is_active is true and 1612 description %(fragment_condition)s and 1613 %(ctxt_pat)s 1614 order by description) 1615 1616 union 1617 1618 (select pk_health_issue, description || _(' (inactive)'), 2 1619 from clin.v_health_issues where 1620 is_active is false and 1621 description %(fragment_condition)s and 1622 %(ctxt_pat)s 1623 order by description)""" 1624 ], 1625 context = ctxt 1626 ) 1627 1628 try: kwargs['patient_id'] 1629 except KeyError: kwargs['patient_id'] = None 1630 1631 if kwargs['patient_id'] is None: 1632 self.use_current_patient = True 1633 self.__register_patient_change_signals() 1634 pat = gmPerson.gmCurrentPatient() 1635 if pat.connected: 1636 mp.set_context('pat', pat.ID) 1637 else: 1638 self.use_current_patient = False 1639 self.__patient_id = int(kwargs['patient_id']) 1640 mp.set_context('pat', self.__patient_id) 1641 1642 del kwargs['patient_id'] 1643 1644 gmPhraseWheel.cPhraseWheel.__init__ ( 1645 self, 1646 *args, 1647 **kwargs 1648 ) 1649 self.matcher = mp
1650 #-------------------------------------------------------- 1651 # external API 1652 #--------------------------------------------------------
1653 - def set_patient(self, patient_id=None):
1654 if self.use_current_patient: 1655 return False 1656 self.__patient_id = int(patient_id) 1657 self.set_context('pat', self.__patient_id) 1658 return True
1659 #--------------------------------------------------------
1660 - def GetData(self, can_create=False, is_open=False):
1661 if self.data is None: 1662 if can_create: 1663 issue_name = self.GetValue().strip() 1664 1665 if self.use_current_patient: 1666 pat = gmPerson.gmCurrentPatient() 1667 else: 1668 pat = gmPerson.cPatient(aPK_obj=self.__patient_id) 1669 emr = pat.get_emr() 1670 1671 issue = emr.add_health_issue(issue_name = issue_name) 1672 if issue is None: 1673 self.data = None 1674 else: 1675 self.data = issue['pk_health_issue'] 1676 1677 return gmPhraseWheel.cPhraseWheel.GetData(self)
1678 #-------------------------------------------------------- 1679 # internal API 1680 #--------------------------------------------------------
1682 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1683 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1684 #--------------------------------------------------------
1685 - def _pre_patient_selection(self):
1686 return True
1687 #--------------------------------------------------------
1688 - def _post_patient_selection(self):
1689 if self.use_current_patient: 1690 patient = gmPerson.gmCurrentPatient() 1691 self.set_context('pat', patient.ID) 1692 return True
1693 #------------------------------------------------------------
1694 -class cIssueSelectionDlg(wxgIssueSelectionDlg.wxgIssueSelectionDlg):
1695
1696 - def __init__(self, *args, **kwargs):
1697 try: 1698 msg = kwargs['message'] 1699 except KeyError: 1700 msg = None 1701 del kwargs['message'] 1702 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs) 1703 if msg is not None: 1704 self._lbl_message.SetLabel(label=msg)
1705 #--------------------------------------------------------
1706 - def _on_OK_button_pressed(self, event):
1707 event.Skip() 1708 pk_issue = self._PhWheel_issue.GetData(can_create=True) 1709 if pk_issue is None: 1710 gmGuiHelpers.gm_show_error ( 1711 _('Cannot create new health issue:\n [%(issue)s]') % {'issue': self._PhWheel_issue.GetValue().strip()}, 1712 _('Selecting health issue') 1713 ) 1714 return False 1715 return True
1716 #------------------------------------------------------------ 1717 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl 1718
1719 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1720 """Panel encapsulating health issue edit area functionality.""" 1721
1722 - def __init__(self, *args, **kwargs):
1723 1724 try: 1725 issue = kwargs['issue'] 1726 except KeyError: 1727 issue = None 1728 1729 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs) 1730 1731 gmEditArea.cGenericEditAreaMixin.__init__(self) 1732 1733 # FIXME: include more sources: coding systems/other database columns 1734 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1735 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"] 1736 ) 1737 mp.setThresholds(1, 3, 5) 1738 self._PRW_condition.matcher = mp 1739 1740 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1741 queries = [u""" 1742 select distinct on (grouping) grouping, grouping from ( 1743 1744 select rank, grouping from (( 1745 1746 select 1747 grouping, 1748 1 as rank 1749 from 1750 clin.health_issue 1751 where 1752 grouping %%(fragment_condition)s 1753 and 1754 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter) 1755 1756 ) union ( 1757 1758 select 1759 grouping, 1760 2 as rank 1761 from 1762 clin.health_issue 1763 where 1764 grouping %%(fragment_condition)s 1765 1766 )) as union_result 1767 1768 order by rank 1769 1770 ) as order_result 1771 1772 limit 50""" % gmPerson.gmCurrentPatient().ID 1773 ] 1774 ) 1775 mp.setThresholds(1, 3, 5) 1776 self._PRW_grouping.matcher = mp 1777 1778 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted) 1779 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted) 1780 1781 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted) 1782 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted) 1783 1784 self._PRW_year_noted.Enable(True) 1785 1786 self.data = issue
1787 #---------------------------------------------------------------- 1788 # generic Edit Area mixin API 1789 #----------------------------------------------------------------
1790 - def _valid_for_save(self):
1791 1792 if self._PRW_condition.GetValue().strip() == '': 1793 self._PRW_condition.display_as_valid(False) 1794 self._PRW_condition.SetFocus() 1795 return False 1796 self._PRW_condition.display_as_valid(True) 1797 self._PRW_condition.Refresh() 1798 1799 # FIXME: sanity check age/year diagnosed 1800 age_noted = self._PRW_age_noted.GetValue().strip() 1801 if age_noted != '': 1802 if gmDateTime.str2interval(str_interval = age_noted) is None: 1803 self._PRW_age_noted.display_as_valid(False) 1804 self._PRW_age_noted.SetFocus() 1805 return False 1806 self._PRW_age_noted.display_as_valid(True) 1807 1808 return True
1809 #----------------------------------------------------------------
1810 - def _save_as_new(self):
1811 pat = gmPerson.gmCurrentPatient() 1812 emr = pat.get_emr() 1813 1814 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip()) 1815 1816 side = u'' 1817 if self._ChBOX_left.GetValue(): 1818 side += u's' 1819 if self._ChBOX_right.GetValue(): 1820 side += u'd' 1821 issue['laterality'] = side 1822 1823 issue['summary'] = self._TCTRL_summary.GetValue().strip() 1824 issue['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1825 issue['grouping'] = self._PRW_grouping.GetValue().strip() 1826 issue['is_active'] = self._ChBOX_active.GetValue() 1827 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue() 1828 issue['is_confidential'] = self._ChBOX_confidential.GetValue() 1829 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue() 1830 1831 age_noted = self._PRW_age_noted.GetData() 1832 if age_noted is not None: 1833 issue['age_noted'] = age_noted 1834 1835 issue.save() 1836 1837 self.data = issue 1838 return True
1839 #----------------------------------------------------------------
1840 - def _save_as_update(self):
1841 1842 self.data['description'] = self._PRW_condition.GetValue().strip() 1843 1844 side = u'' 1845 if self._ChBOX_left.GetValue(): 1846 side += u's' 1847 if self._ChBOX_right.GetValue(): 1848 side += u'd' 1849 self.data['laterality'] = side 1850 1851 self.data['summary'] = self._TCTRL_summary.GetValue().strip() 1852 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1853 self.data['grouping'] = self._PRW_grouping.GetValue().strip() 1854 self.data['is_active'] = bool(self._ChBOX_active.GetValue()) 1855 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue()) 1856 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue()) 1857 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue()) 1858 1859 age_noted = self._PRW_age_noted.GetData() 1860 if age_noted is not None: 1861 self.data['age_noted'] = age_noted 1862 1863 self.data.save() 1864 1865 # FIXME: handle is_operation 1866 return True
1867 #----------------------------------------------------------------
1868 - def _refresh_as_new(self):
1869 self._PRW_condition.SetText() 1870 self._ChBOX_left.SetValue(0) 1871 self._ChBOX_right.SetValue(0) 1872 self._PRW_classification.SetText() 1873 self._PRW_grouping.SetText() 1874 self._TCTRL_summary.SetValue(u'') 1875 self._PRW_age_noted.SetText() 1876 self._PRW_year_noted.SetText() 1877 self._ChBOX_active.SetValue(0) 1878 self._ChBOX_relevant.SetValue(1) 1879 self._ChBOX_is_operation.SetValue(0) 1880 self._ChBOX_confidential.SetValue(0) 1881 self._ChBOX_caused_death.SetValue(0) 1882 1883 return True
1884 #----------------------------------------------------------------
1885 - def _refresh_from_existing(self):
1886 self._PRW_condition.SetText(self.data['description']) 1887 1888 lat = gmTools.coalesce(self.data['laterality'], '') 1889 if lat.find('s') == -1: 1890 self._ChBOX_left.SetValue(0) 1891 else: 1892 self._ChBOX_left.SetValue(1) 1893 if lat.find('d') == -1: 1894 self._ChBOX_right.SetValue(0) 1895 else: 1896 self._ChBOX_right.SetValue(1) 1897 1898 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification']) 1899 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u'')) 1900 self._TCTRL_summary.SetValue(gmTools.coalesce(self.data['summary'], u'')) 1901 1902 if self.data['age_noted'] is None: 1903 self._PRW_age_noted.SetText() 1904 else: 1905 self._PRW_age_noted.SetText ( 1906 value = '%sd' % self.data['age_noted'].days, 1907 data = self.data['age_noted'] 1908 ) 1909 1910 self._ChBOX_active.SetValue(self.data['is_active']) 1911 self._ChBOX_relevant.SetValue(self.data['clinically_relevant']) 1912 self._ChBOX_is_operation.SetValue(0) # FIXME 1913 self._ChBOX_confidential.SetValue(self.data['is_confidential']) 1914 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death']) 1915 1916 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ... 1917 # self._PRW_age_noted.SetFocus() 1918 # self._PRW_condition.SetFocus() 1919 1920 return True
1921 #----------------------------------------------------------------
1923 return self._refresh_as_new()
1924 #-------------------------------------------------------- 1925 # internal helpers 1926 #--------------------------------------------------------
1927 - def _on_leave_age_noted(self, *args, **kwargs):
1928 1929 if not self._PRW_age_noted.IsModified(): 1930 return True 1931 1932 str_age = self._PRW_age_noted.GetValue().strip() 1933 1934 if str_age == u'': 1935 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1936 return True 1937 1938 age = gmDateTime.str2interval(str_interval = str_age) 1939 1940 if age is None: 1941 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age) 1942 self._PRW_age_noted.SetBackgroundColour('pink') 1943 self._PRW_age_noted.Refresh() 1944 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1945 return True 1946 1947 pat = gmPerson.gmCurrentPatient() 1948 if pat['dob'] is not None: 1949 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob'] 1950 1951 if age >= max_age: 1952 gmDispatcher.send ( 1953 signal = 'statustext', 1954 msg = _( 1955 'Health issue cannot have been noted at age %s. Patient is only %s old.' 1956 ) % (age, pat.get_medical_age()) 1957 ) 1958 self._PRW_age_noted.SetBackgroundColour('pink') 1959 self._PRW_age_noted.Refresh() 1960 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1961 return True 1962 1963 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1964 self._PRW_age_noted.Refresh() 1965 self._PRW_age_noted.SetData(data=age) 1966 1967 if pat['dob'] is not None: 1968 fts = gmDateTime.cFuzzyTimestamp ( 1969 timestamp = pat['dob'] + age, 1970 accuracy = gmDateTime.acc_months 1971 ) 1972 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts) 1973 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB 1974 #wx.CallAfter(self._ChBOX_active.SetFocus) 1975 # if we do the following instead it will take us to the save/update button ... 1976 #wx.CallAfter(self.Navigate) 1977 1978 return True
1979 #--------------------------------------------------------
1980 - def _on_leave_year_noted(self, *args, **kwargs):
1981 1982 if not self._PRW_year_noted.IsModified(): 1983 return True 1984 1985 year_noted = self._PRW_year_noted.GetData() 1986 1987 if year_noted is None: 1988 if self._PRW_year_noted.GetValue().strip() == u'': 1989 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1990 return True 1991 self._PRW_year_noted.SetBackgroundColour('pink') 1992 self._PRW_year_noted.Refresh() 1993 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1994 return True 1995 1996 year_noted = year_noted.get_pydt() 1997 1998 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo): 1999 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.')) 2000 self._PRW_year_noted.SetBackgroundColour('pink') 2001 self._PRW_year_noted.Refresh() 2002 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2003 return True 2004 2005 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2006 self._PRW_year_noted.Refresh() 2007 2008 pat = gmPerson.gmCurrentPatient() 2009 if pat['dob'] is not None: 2010 issue_age = year_noted - pat['dob'] 2011 str_age = gmDateTime.format_interval_medically(interval = issue_age) 2012 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age) 2013 2014 return True
2015 #--------------------------------------------------------
2016 - def _on_modified_age_noted(self, *args, **kwargs):
2017 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 2018 return True
2019 #--------------------------------------------------------
2020 - def _on_modified_year_noted(self, *args, **kwargs):
2021 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 2022 return True
2023 #================================================================ 2024 # diagnostic certainty related widgets/functions 2025 #----------------------------------------------------------------
2026 -class cDiagnosticCertaintyClassificationPhraseWheel(gmPhraseWheel.cPhraseWheel):
2027
2028 - def __init__(self, *args, **kwargs):
2029 2030 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2031 2032 self.selection_only = False # can be NULL, too 2033 2034 mp = gmMatchProvider.cMatchProvider_FixedList ( 2035 aSeq = [ 2036 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1}, 2037 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1}, 2038 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1}, 2039 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1} 2040 ] 2041 ) 2042 mp.setThresholds(1, 2, 4) 2043 self.matcher = mp 2044 2045 self.SetToolTipString(_( 2046 "The diagnostic classification or grading of this assessment.\n" 2047 "\n" 2048 "This documents how certain one is about this being a true diagnosis." 2049 ))
2050 #================================================================ 2051 # MAIN 2052 #---------------------------------------------------------------- 2053 if __name__ == '__main__': 2054 2055 #================================================================
2056 - class testapp (wx.App):
2057 """ 2058 Test application for testing EMR struct widgets 2059 """ 2060 #--------------------------------------------------------
2061 - def OnInit (self):
2062 """ 2063 Create test application UI 2064 """ 2065 frame = wx.Frame ( 2066 None, 2067 -4, 2068 'Testing EMR struct widgets', 2069 size=wx.Size(600, 400), 2070 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 2071 ) 2072 filemenu= wx.Menu() 2073 filemenu.AppendSeparator() 2074 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application") 2075 2076 # Creating the menubar. 2077 menuBar = wx.MenuBar() 2078 menuBar.Append(filemenu,"&File") 2079 2080 frame.SetMenuBar(menuBar) 2081 2082 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"), 2083 wx.DefaultPosition, wx.DefaultSize, 0 ) 2084 2085 # event handlers 2086 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow) 2087 2088 # patient EMR 2089 self.__pat = gmPerson.gmCurrentPatient() 2090 2091 frame.Show(1) 2092 return 1
2093 #--------------------------------------------------------
2094 - def OnCloseWindow (self, e):
2095 """ 2096 Close test aplication 2097 """ 2098 self.ExitMainLoop ()
2099 #----------------------------------------------------------------
2100 - def test_encounter_edit_area_panel():
2101 app = wx.PyWidgetTester(size = (200, 300)) 2102 emr = pat.get_emr() 2103 enc = emr.active_encounter 2104 #enc = gmEMRStructItems.cEncounter(1) 2105 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc) 2106 app.frame.Show(True) 2107 app.MainLoop() 2108 return
2109 #----------------------------------------------------------------
2110 - def test_encounter_edit_area_dialog():
2111 app = wx.PyWidgetTester(size = (200, 300)) 2112 emr = pat.get_emr() 2113 enc = emr.active_encounter 2114 #enc = gmEMRStructItems.cEncounter(1) 2115 2116 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc) 2117 dlg.ShowModal()
2118 2119 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc) 2120 # app.frame.Show(True) 2121 # app.MainLoop() 2122 #----------------------------------------------------------------
2123 - def test_epsiode_edit_area_pnl():
2124 app = wx.PyWidgetTester(size = (200, 300)) 2125 emr = pat.get_emr() 2126 epi = emr.get_episodes()[0] 2127 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi) 2128 app.frame.Show(True) 2129 app.MainLoop()
2130 #----------------------------------------------------------------
2131 - def test_episode_edit_area_dialog():
2132 app = wx.PyWidgetTester(size = (200, 300)) 2133 emr = pat.get_emr() 2134 epi = emr.get_episodes()[0] 2135 edit_episode(parent=app.frame, episode=epi)
2136 #----------------------------------------------------------------
2137 - def test_hospital_stay_prw():
2138 app = wx.PyWidgetTester(size = (400, 40)) 2139 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2140 app.MainLoop()
2141 #----------------------------------------------------------------
2142 - def test_episode_selection_prw():
2143 app = wx.PyWidgetTester(size = (400, 40)) 2144 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2145 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID) 2146 app.MainLoop()
2147 #----------------------------------------------------------------
2148 - def test_health_issue_edit_area_dlg():
2149 app = wx.PyWidgetTester(size = (200, 300)) 2150 edit_health_issue(parent=app.frame, issue=None)
2151 #----------------------------------------------------------------
2152 - def test_health_issue_edit_area_pnl():
2153 app = wx.PyWidgetTester(size = (200, 300)) 2154 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400)) 2155 app.MainLoop()
2156 #----------------------------------------------------------------
2157 - def test_edit_procedure():
2158 app = wx.PyWidgetTester(size = (200, 300)) 2159 edit_procedure(parent=app.frame)
2160 #================================================================ 2161 2162 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 2163 2164 gmI18N.activate_locale() 2165 gmI18N.install_domain() 2166 gmDateTime.init() 2167 2168 # obtain patient 2169 pat = gmPersonSearch.ask_for_patient() 2170 if pat is None: 2171 print "No patient. Exiting gracefully..." 2172 sys.exit(0) 2173 gmPatSearchWidgets.set_active_patient(patient=pat) 2174 2175 # try: 2176 # lauch emr dialogs test application 2177 # app = testapp(0) 2178 # app.MainLoop() 2179 # except StandardError: 2180 # _log.exception("unhandled exception caught !") 2181 # but re-raise them 2182 # raise 2183 2184 #test_encounter_edit_area_panel() 2185 #test_encounter_edit_area_dialog() 2186 #test_epsiode_edit_area_pnl() 2187 #test_episode_edit_area_dialog() 2188 #test_health_issue_edit_area_dlg() 2189 #test_episode_selection_prw() 2190 #test_hospital_stay_prw() 2191 test_edit_procedure() 2192 2193 #================================================================ 2194