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