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 1225 self.__is_open = is_open 1226 1227 # if self.data is None: 1228 # if can_create: 1229 # epi_name = self.GetValue().strip() 1230 # if epi_name == u'': 1231 # gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True) 1232 # _log.debug('cannot create episode without name') 1233 # else: 1234 # if self.use_current_patient: 1235 # pat = gmPerson.gmCurrentPatient() 1236 # else: 1237 # pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 1238 # 1239 # emr = pat.get_emr() 1240 # epi = emr.add_episode(episode_name = epi_name, is_open = is_open) 1241 # if epi is None: 1242 # self.data = None 1243 # else: 1244 # self.data = epi['pk_episode'] 1245 1246 return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1247 #--------------------------------------------------------
1248 - def _create_data(self):
1249 1250 epi_name = self.GetValue().strip() 1251 if epi_name == u'': 1252 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True) 1253 _log.debug('cannot create episode without name') 1254 return 1255 1256 if self.use_current_patient: 1257 pat = gmPerson.gmCurrentPatient() 1258 else: 1259 pat = gmPerson.cPatient(aPK_obj = self.__patient_id) 1260 1261 emr = pat.get_emr() 1262 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open) 1263 if epi is None: 1264 self.data = None 1265 else: 1266 self.data = epi['pk_episode']
1267 #--------------------------------------------------------
1268 - def _data2instance(self):
1269 return gmEMRStructItems.cEpisode(aPK_obj = self.data)
1270 #-------------------------------------------------------- 1271 # internal API 1272 #--------------------------------------------------------
1274 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1275 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1276 #--------------------------------------------------------
1277 - def _pre_patient_selection(self):
1278 return True
1279 #--------------------------------------------------------
1280 - def _post_patient_selection(self):
1281 if self.use_current_patient: 1282 patient = gmPerson.gmCurrentPatient() 1283 self.set_context('pat', patient.ID) 1284 return True
1285 #----------------------------------------------------------------
1286 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1287
1288 - def __init__(self, *args, **kwargs):
1289 1290 try: 1291 episode = kwargs['episode'] 1292 del kwargs['episode'] 1293 except KeyError: 1294 episode = None 1295 1296 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs) 1297 gmEditArea.cGenericEditAreaMixin.__init__(self) 1298 1299 self.data = episode
1300 #---------------------------------------------------------------- 1301 # generic Edit Area mixin API 1302 #----------------------------------------------------------------
1303 - def _valid_for_save(self):
1304 1305 errors = False 1306 1307 if len(self._PRW_description.GetValue().strip()) == 0: 1308 errors = True 1309 self._PRW_description.display_as_valid(False) 1310 self._PRW_description.SetFocus() 1311 else: 1312 self._PRW_description.display_as_valid(True) 1313 self._PRW_description.Refresh() 1314 1315 return not errors
1316 #----------------------------------------------------------------
1317 - def _save_as_new(self):
1318 1319 pat = gmPerson.gmCurrentPatient() 1320 emr = pat.get_emr() 1321 1322 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip()) 1323 epi['episode_open'] = not self._CHBOX_closed.IsChecked() 1324 epi['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1325 1326 issue_name = self._PRW_issue.GetValue().strip() 1327 if len(issue_name) != 0: 1328 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1329 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue']) 1330 1331 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False): 1332 gmDispatcher.send ( 1333 signal = 'statustext', 1334 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1335 epi['description'], 1336 issue['description'] 1337 ) 1338 ) 1339 gmEMRStructItems.delete_episode(episode = epi) 1340 return False 1341 1342 epi.save() 1343 1344 self.data = epi 1345 return True
1346 #----------------------------------------------------------------
1347 - def _save_as_update(self):
1348 1349 self.data['description'] = self._PRW_description.GetValue().strip() 1350 self.data['episode_open'] = not self._CHBOX_closed.IsChecked() 1351 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1352 1353 issue_name = self._PRW_issue.GetValue().strip() 1354 if len(issue_name) != 0: 1355 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) 1356 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue']) 1357 1358 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False): 1359 gmDispatcher.send ( 1360 signal = 'statustext', 1361 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( 1362 self.data['description'], 1363 issue['description'] 1364 ) 1365 ) 1366 return False 1367 1368 self.data.save() 1369 return True
1370 #----------------------------------------------------------------
1371 - def _refresh_as_new(self):
1372 if self.data is None: 1373 ident = gmPerson.gmCurrentPatient() 1374 else: 1375 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1376 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1377 self._PRW_issue.SetText() 1378 self._PRW_description.SetText() 1379 self._PRW_classification.SetText() 1380 self._CHBOX_closed.SetValue(False)
1381 #----------------------------------------------------------------
1382 - def _refresh_from_existing(self):
1383 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient']) 1384 self._TCTRL_patient.SetValue(ident.get_description_gender()) 1385 1386 if self.data['pk_health_issue'] is not None: 1387 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue']) 1388 1389 self._PRW_description.SetText(self.data['description'], data=self.data['description']) 1390 1391 if self.data['diagnostic_certainty_classification'] is not None: 1392 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification']) 1393 1394 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1395 #----------------------------------------------------------------
1397 self._refresh_as_new()
1398 #================================================================ 1399 # health issue related widgets/functions 1400 #----------------------------------------------------------------
1401 -def edit_health_issue(parent=None, issue=None):
1402 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1) 1403 ea.data = issue 1404 ea.mode = gmTools.coalesce(issue, 'new', 'edit') 1405 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1406 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue'))) 1407 if dlg.ShowModal() == wx.ID_OK: 1408 return True 1409 return False
1410 #----------------------------------------------------------------
1411 -class cIssueListSelectorDlg(gmListWidgets.cGenericListSelectorDlg):
1412 1413 # FIXME: support pre-selection 1414
1415 - def __init__(self, *args, **kwargs):
1416 1417 issues = kwargs['issues'] 1418 del kwargs['issues'] 1419 1420 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs) 1421 1422 self.SetTitle(_('Select the health issues you are interested in ...')) 1423 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u'']) 1424 1425 for issue in issues: 1426 if issue['is_confidential']: 1427 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential')) 1428 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED')) 1429 else: 1430 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'') 1431 1432 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description']) 1433 if issue['clinically_relevant']: 1434 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant')) 1435 if issue['is_active']: 1436 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active')) 1437 if issue['is_cause_of_death']: 1438 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal')) 1439 1440 self._LCTRL_items.set_column_widths() 1441 self._LCTRL_items.set_data(data = issues)
1442 #----------------------------------------------------------------
1443 -class cIssueSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
1444 """Let the user select a health issue. 1445 1446 The user can select a health issue from the existing issues 1447 of a patient. Selection is done with a phrasewheel so the user 1448 can type the issue name and matches will be shown. Typing 1449 "*" will show the entire list of issues. Inactive issues 1450 will be marked as such. If the user types an issue name not 1451 in the list of existing issues a new issue can be created 1452 from it if the programmer activated that feature. 1453 1454 If keyword <patient_id> is set to None or left out the control 1455 will listen to patient change signals and therefore act on 1456 gmPerson.gmCurrentPatient() changes. 1457 """
1458 - def __init__(self, *args, **kwargs):
1459 1460 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}} 1461 1462 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1463 # FIXME: consider clin.health_issue.clinically_relevant 1464 queries = [u""" 1465 (select pk_health_issue, description, 1 1466 from clin.v_health_issues where 1467 is_active is true and 1468 description %(fragment_condition)s and 1469 %(ctxt_pat)s 1470 order by description) 1471 1472 union 1473 1474 (select pk_health_issue, description || _(' (inactive)'), 2 1475 from clin.v_health_issues where 1476 is_active is false and 1477 description %(fragment_condition)s and 1478 %(ctxt_pat)s 1479 order by description)""" 1480 ], 1481 context = ctxt 1482 ) 1483 1484 try: kwargs['patient_id'] 1485 except KeyError: kwargs['patient_id'] = None 1486 1487 if kwargs['patient_id'] is None: 1488 self.use_current_patient = True 1489 self.__register_patient_change_signals() 1490 pat = gmPerson.gmCurrentPatient() 1491 if pat.connected: 1492 mp.set_context('pat', pat.ID) 1493 else: 1494 self.use_current_patient = False 1495 self.__patient_id = int(kwargs['patient_id']) 1496 mp.set_context('pat', self.__patient_id) 1497 1498 del kwargs['patient_id'] 1499 1500 gmPhraseWheel.cPhraseWheel.__init__ ( 1501 self, 1502 *args, 1503 **kwargs 1504 ) 1505 self.matcher = mp
1506 #-------------------------------------------------------- 1507 # external API 1508 #--------------------------------------------------------
1509 - def set_patient(self, patient_id=None):
1510 if self.use_current_patient: 1511 return False 1512 self.__patient_id = int(patient_id) 1513 self.set_context('pat', self.__patient_id) 1514 return True
1515 #--------------------------------------------------------
1516 - def GetData(self, can_create=False, is_open=False):
1517 if self.data is None: 1518 if can_create: 1519 issue_name = self.GetValue().strip() 1520 1521 if self.use_current_patient: 1522 pat = gmPerson.gmCurrentPatient() 1523 else: 1524 pat = gmPerson.cPatient(aPK_obj=self.__patient_id) 1525 emr = pat.get_emr() 1526 1527 issue = emr.add_health_issue(issue_name = issue_name) 1528 if issue is None: 1529 self.data = None 1530 else: 1531 self.data = issue['pk_health_issue'] 1532 1533 return gmPhraseWheel.cPhraseWheel.GetData(self)
1534 #-------------------------------------------------------- 1535 # internal API 1536 #--------------------------------------------------------
1538 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection') 1539 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1540 #--------------------------------------------------------
1541 - def _pre_patient_selection(self):
1542 return True
1543 #--------------------------------------------------------
1544 - def _post_patient_selection(self):
1545 if self.use_current_patient: 1546 patient = gmPerson.gmCurrentPatient() 1547 self.set_context('pat', patient.ID) 1548 return True
1549 #------------------------------------------------------------
1550 -class cIssueSelectionDlg(wxgIssueSelectionDlg.wxgIssueSelectionDlg):
1551
1552 - def __init__(self, *args, **kwargs):
1553 try: 1554 msg = kwargs['message'] 1555 except KeyError: 1556 msg = None 1557 del kwargs['message'] 1558 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs) 1559 if msg is not None: 1560 self._lbl_message.SetLabel(label=msg)
1561 #--------------------------------------------------------
1562 - def _on_OK_button_pressed(self, event):
1563 event.Skip() 1564 pk_issue = self._PhWheel_issue.GetData(can_create=True) 1565 if pk_issue is None: 1566 gmGuiHelpers.gm_show_error ( 1567 _('Cannot create new health issue:\n [%(issue)s]') % {'issue': self._PhWheel_issue.GetValue().strip()}, 1568 _('Selecting health issue') 1569 ) 1570 return False 1571 return True
1572 #------------------------------------------------------------
1573 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1574 """Panel encapsulating health issue edit area functionality.""" 1575
1576 - def __init__(self, *args, **kwargs):
1577 1578 try: 1579 issue = kwargs['issue'] 1580 except KeyError: 1581 issue = None 1582 1583 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs) 1584 1585 gmEditArea.cGenericEditAreaMixin.__init__(self) 1586 1587 # FIXME: include more sources: coding systems/other database columns 1588 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1589 queries = [u"select distinct on (description) description, description from clin.health_issue where description %(fragment_condition)s limit 50"] 1590 ) 1591 mp.setThresholds(1, 3, 5) 1592 self._PRW_condition.matcher = mp 1593 1594 mp = gmMatchProvider.cMatchProvider_SQL2 ( 1595 queries = [u""" 1596 select distinct on (grouping) grouping, grouping from ( 1597 1598 select rank, grouping from (( 1599 1600 select 1601 grouping, 1602 1 as rank 1603 from 1604 clin.health_issue 1605 where 1606 grouping %%(fragment_condition)s 1607 and 1608 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter) 1609 1610 ) union ( 1611 1612 select 1613 grouping, 1614 2 as rank 1615 from 1616 clin.health_issue 1617 where 1618 grouping %%(fragment_condition)s 1619 1620 )) as union_result 1621 1622 order by rank 1623 1624 ) as order_result 1625 1626 limit 50""" % gmPerson.gmCurrentPatient().ID 1627 ] 1628 ) 1629 mp.setThresholds(1, 3, 5) 1630 self._PRW_grouping.matcher = mp 1631 1632 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted) 1633 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted) 1634 1635 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted) 1636 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted) 1637 1638 self.data = issue
1639 #---------------------------------------------------------------- 1640 # generic Edit Area mixin API 1641 #----------------------------------------------------------------
1642 - def _valid_for_save(self):
1643 1644 if self._PRW_condition.GetValue().strip() == '': 1645 self._PRW_condition.display_as_valid(False) 1646 self._PRW_condition.SetFocus() 1647 return False 1648 self._PRW_condition.display_as_valid(True) 1649 self._PRW_condition.Refresh() 1650 1651 # FIXME: sanity check age/year diagnosed 1652 age_noted = self._PRW_age_noted.GetValue().strip() 1653 if age_noted != '': 1654 if gmDateTime.str2interval(str_interval = age_noted) is None: 1655 self._PRW_age_noted.display_as_valid(False) 1656 self._PRW_age_noted.SetFocus() 1657 return False 1658 self._PRW_age_noted.display_as_valid(True) 1659 1660 return True
1661 #----------------------------------------------------------------
1662 - def _save_as_new(self):
1663 pat = gmPerson.gmCurrentPatient() 1664 emr = pat.get_emr() 1665 1666 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip()) 1667 1668 side = u'' 1669 if self._ChBOX_left.GetValue(): 1670 side += u's' 1671 if self._ChBOX_right.GetValue(): 1672 side += u'd' 1673 issue['laterality'] = side 1674 1675 issue['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1676 issue['grouping'] = self._PRW_grouping.GetValue().strip() 1677 issue['is_active'] = self._ChBOX_active.GetValue() 1678 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue() 1679 issue['is_confidential'] = self._ChBOX_confidential.GetValue() 1680 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue() 1681 1682 age_noted = self._PRW_age_noted.GetData() 1683 if age_noted is not None: 1684 issue['age_noted'] = age_noted 1685 1686 issue.save() 1687 1688 narr = self._TCTRL_notes.GetValue().strip() 1689 if narr != u'': 1690 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = issue['pk_health_issue']) 1691 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi) 1692 1693 self.data = issue 1694 1695 return True
1696 #----------------------------------------------------------------
1697 - def _save_as_update(self):
1698 # update self.data and save the changes 1699 1700 self.data['description'] = self._PRW_condition.GetValue().strip() 1701 1702 side = u'' 1703 if self._ChBOX_left.GetValue(): 1704 side += u's' 1705 if self._ChBOX_right.GetValue(): 1706 side += u'd' 1707 self.data['laterality'] = side 1708 1709 self.data['diagnostic_certainty_classification'] = self._PRW_classification.GetData() 1710 self.data['grouping'] = self._PRW_grouping.GetValue().strip() 1711 self.data['is_active'] = bool(self._ChBOX_active.GetValue()) 1712 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue()) 1713 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue()) 1714 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue()) 1715 1716 age_noted = self._PRW_age_noted.GetData() 1717 if age_noted is not None: 1718 self.data['age_noted'] = age_noted 1719 1720 self.data.save() 1721 1722 narr = self._TCTRL_notes.GetValue().strip() 1723 if narr != '': 1724 pat = gmPerson.gmCurrentPatient() 1725 emr = pat.get_emr() 1726 epi = emr.add_episode(episode_name = _('inception notes'), pk_health_issue = self.data['pk_health_issue']) 1727 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi) 1728 1729 # FIXME: handle is_operation 1730 return True
1731 #----------------------------------------------------------------
1732 - def _refresh_as_new(self):
1733 self._PRW_condition.SetText() 1734 self._ChBOX_left.SetValue(0) 1735 self._ChBOX_right.SetValue(0) 1736 self._PRW_classification.SetText() 1737 self._PRW_grouping.SetText() 1738 self._TCTRL_notes.SetValue(u'') 1739 self._PRW_age_noted.SetText() 1740 self._PRW_year_noted.SetText() 1741 self._ChBOX_active.SetValue(0) 1742 self._ChBOX_relevant.SetValue(1) 1743 self._ChBOX_is_operation.SetValue(0) 1744 self._ChBOX_confidential.SetValue(0) 1745 self._ChBOX_caused_death.SetValue(0) 1746 1747 return True
1748 #----------------------------------------------------------------
1749 - def _refresh_from_existing(self):
1750 self._PRW_condition.SetText(self.data['description']) 1751 1752 lat = gmTools.coalesce(self.data['laterality'], '') 1753 if lat.find('s') == -1: 1754 self._ChBOX_left.SetValue(0) 1755 else: 1756 self._ChBOX_left.SetValue(1) 1757 if lat.find('d') == -1: 1758 self._ChBOX_right.SetValue(0) 1759 else: 1760 self._ChBOX_right.SetValue(1) 1761 1762 self._PRW_classification.SetData(data = self.data['diagnostic_certainty_classification']) 1763 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u'')) 1764 self._TCTRL_notes.SetValue('') 1765 1766 if self.data['age_noted'] is None: 1767 self._PRW_age_noted.SetText() 1768 else: 1769 self._PRW_age_noted.SetText ( 1770 value = '%sd' % self.data['age_noted'].days, 1771 data = self.data['age_noted'] 1772 ) 1773 1774 self._ChBOX_active.SetValue(self.data['is_active']) 1775 self._ChBOX_relevant.SetValue(self.data['clinically_relevant']) 1776 self._ChBOX_is_operation.SetValue(0) # FIXME 1777 self._ChBOX_confidential.SetValue(self.data['is_confidential']) 1778 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death']) 1779 1780 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ... 1781 # self._PRW_age_noted.SetFocus() 1782 # self._PRW_condition.SetFocus() 1783 1784 return True
1785 #----------------------------------------------------------------
1787 return self._refresh_as_new()
1788 #-------------------------------------------------------- 1789 # internal helpers 1790 #--------------------------------------------------------
1791 - def _on_leave_age_noted(self, *args, **kwargs):
1792 1793 if not self._PRW_age_noted.IsModified(): 1794 return True 1795 1796 str_age = self._PRW_age_noted.GetValue().strip() 1797 1798 if str_age == u'': 1799 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1800 return True 1801 1802 age = gmDateTime.str2interval(str_interval = str_age) 1803 1804 if age is None: 1805 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age) 1806 self._PRW_age_noted.SetBackgroundColour('pink') 1807 self._PRW_age_noted.Refresh() 1808 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1809 return True 1810 1811 pat = gmPerson.gmCurrentPatient() 1812 if pat['dob'] is not None: 1813 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob'] 1814 1815 if age >= max_age: 1816 gmDispatcher.send ( 1817 signal = 'statustext', 1818 msg = _( 1819 'Health issue cannot have been noted at age %s. Patient is only %s old.' 1820 ) % (age, pat.get_medical_age()) 1821 ) 1822 self._PRW_age_noted.SetBackgroundColour('pink') 1823 self._PRW_age_noted.Refresh() 1824 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1825 return True 1826 1827 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1828 self._PRW_age_noted.Refresh() 1829 self._PRW_age_noted.SetData(data=age) 1830 1831 if pat['dob'] is not None: 1832 fts = gmDateTime.cFuzzyTimestamp ( 1833 timestamp = pat['dob'] + age, 1834 accuracy = gmDateTime.acc_months 1835 ) 1836 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts) 1837 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB 1838 #wx.CallAfter(self._ChBOX_active.SetFocus) 1839 # if we do the following instead it will take us to the save/update button ... 1840 #wx.CallAfter(self.Navigate) 1841 1842 return True
1843 #--------------------------------------------------------
1844 - def _on_leave_year_noted(self, *args, **kwargs):
1845 1846 if not self._PRW_year_noted.IsModified(): 1847 return True 1848 1849 year_noted = self._PRW_year_noted.GetData() 1850 1851 if year_noted is None: 1852 if self._PRW_year_noted.GetValue().strip() == u'': 1853 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1854 return True 1855 self._PRW_year_noted.SetBackgroundColour('pink') 1856 self._PRW_year_noted.Refresh() 1857 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1858 return True 1859 1860 year_noted = year_noted.get_pydt() 1861 1862 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo): 1863 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.')) 1864 self._PRW_year_noted.SetBackgroundColour('pink') 1865 self._PRW_year_noted.Refresh() 1866 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1867 return True 1868 1869 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1870 self._PRW_year_noted.Refresh() 1871 1872 pat = gmPerson.gmCurrentPatient() 1873 if pat['dob'] is not None: 1874 issue_age = year_noted - pat['dob'] 1875 str_age = gmDateTime.format_interval_medically(interval = issue_age) 1876 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age) 1877 1878 return True
1879 #--------------------------------------------------------
1880 - def _on_modified_age_noted(self, *args, **kwargs):
1881 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True) 1882 return True
1883 #--------------------------------------------------------
1884 - def _on_modified_year_noted(self, *args, **kwargs):
1885 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True) 1886 return True
1887 #================================================================ 1888 # diagnostic certainty related widgets/functions 1889 #----------------------------------------------------------------
1890 -class cDiagnosticCertaintyClassificationPhraseWheel(gmPhraseWheel.cPhraseWheel):
1891
1892 - def __init__(self, *args, **kwargs):
1893 1894 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1895 1896 self.selection_only = False # can be NULL, too 1897 1898 mp = gmMatchProvider.cMatchProvider_FixedList ( 1899 aSeq = [ 1900 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1}, 1901 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1}, 1902 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1}, 1903 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1} 1904 ] 1905 ) 1906 mp.setThresholds(1, 2, 4) 1907 self.matcher = mp 1908 1909 self.SetToolTipString(_( 1910 "The diagnostic classification or grading of this assessment.\n" 1911 "\n" 1912 "This documents how certain one is about this being a true diagnosis." 1913 ))
1914 #================================================================ 1915 # MAIN 1916 #---------------------------------------------------------------- 1917 if __name__ == '__main__': 1918 1919 #================================================================
1920 - class testapp (wx.App):
1921 """ 1922 Test application for testing EMR struct widgets 1923 """ 1924 #--------------------------------------------------------
1925 - def OnInit (self):
1926 """ 1927 Create test application UI 1928 """ 1929 frame = wx.Frame ( 1930 None, 1931 -4, 1932 'Testing EMR struct widgets', 1933 size=wx.Size(600, 400), 1934 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE 1935 ) 1936 filemenu= wx.Menu() 1937 filemenu.AppendSeparator() 1938 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application") 1939 1940 # Creating the menubar. 1941 menuBar = wx.MenuBar() 1942 menuBar.Append(filemenu,"&File") 1943 1944 frame.SetMenuBar(menuBar) 1945 1946 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"), 1947 wx.DefaultPosition, wx.DefaultSize, 0 ) 1948 1949 # event handlers 1950 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow) 1951 1952 # patient EMR 1953 self.__pat = gmPerson.gmCurrentPatient() 1954 1955 frame.Show(1) 1956 return 1
1957 #--------------------------------------------------------
1958 - def OnCloseWindow (self, e):
1959 """ 1960 Close test aplication 1961 """ 1962 self.ExitMainLoop ()
1963 #----------------------------------------------------------------
1964 - def test_encounter_edit_area_panel():
1965 app = wx.PyWidgetTester(size = (200, 300)) 1966 emr = pat.get_emr() 1967 enc = emr.active_encounter 1968 #enc = gmEMRStructItems.cEncounter(1) 1969 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc) 1970 app.frame.Show(True) 1971 app.MainLoop() 1972 return
1973 #----------------------------------------------------------------
1974 - def test_encounter_edit_area_dialog():
1975 app = wx.PyWidgetTester(size = (200, 300)) 1976 emr = pat.get_emr() 1977 enc = emr.active_encounter 1978 #enc = gmEMRStructItems.cEncounter(1) 1979 1980 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc) 1981 dlg.ShowModal()
1982 1983 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc) 1984 # app.frame.Show(True) 1985 # app.MainLoop() 1986 #----------------------------------------------------------------
1987 - def test_epsiode_edit_area_pnl():
1988 app = wx.PyWidgetTester(size = (200, 300)) 1989 emr = pat.get_emr() 1990 epi = emr.get_episodes()[0] 1991 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi) 1992 app.frame.Show(True) 1993 app.MainLoop()
1994 #----------------------------------------------------------------
1995 - def test_episode_edit_area_dialog():
1996 app = wx.PyWidgetTester(size = (200, 300)) 1997 emr = pat.get_emr() 1998 epi = emr.get_episodes()[0] 1999 edit_episode(parent=app.frame, episode=epi)
2000 #----------------------------------------------------------------
2001 - def test_hospital_stay_prw():
2002 app = wx.PyWidgetTester(size = (400, 40)) 2003 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2004 app.MainLoop()
2005 #----------------------------------------------------------------
2006 - def test_episode_selection_prw():
2007 app = wx.PyWidgetTester(size = (400, 40)) 2008 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20)) 2009 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID) 2010 app.MainLoop()
2011 #----------------------------------------------------------------
2012 - def test_health_issue_edit_area_dlg():
2013 app = wx.PyWidgetTester(size = (200, 300)) 2014 edit_health_issue(parent=app.frame, issue=None)
2015 #----------------------------------------------------------------
2016 - def test_health_issue_edit_area_pnl():
2017 app = wx.PyWidgetTester(size = (200, 300)) 2018 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400)) 2019 app.MainLoop()
2020 #----------------------------------------------------------------
2021 - def test_edit_procedure():
2022 app = wx.PyWidgetTester(size = (200, 300)) 2023 edit_procedure(parent=app.frame)
2024 #================================================================ 2025 2026 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 2027 2028 gmI18N.activate_locale() 2029 gmI18N.install_domain() 2030 gmDateTime.init() 2031 2032 # obtain patient 2033 pat = gmPerson.ask_for_patient() 2034 if pat is None: 2035 print "No patient. Exiting gracefully..." 2036 sys.exit(0) 2037 gmPatSearchWidgets.set_active_patient(patient=pat) 2038 2039 # try: 2040 # lauch emr dialogs test application 2041 # app = testapp(0) 2042 # app.MainLoop() 2043 # except StandardError: 2044 # _log.exception("unhandled exception caught !") 2045 # but re-raise them 2046 # raise 2047 2048 #test_encounter_edit_area_panel() 2049 #test_encounter_edit_area_dialog() 2050 #test_epsiode_edit_area_pnl() 2051 #test_episode_edit_area_dialog() 2052 #test_health_issue_edit_area_dlg() 2053 #test_episode_selection_prw() 2054 #test_hospital_stay_prw() 2055 test_edit_procedure() 2056 2057 #================================================================ 2058