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