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