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