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