Package Gnumed :: Package wxpython :: Module gmEMRStructWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmEMRStructWidgets

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