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

Source Code for Module Gnumed.wxpython.gmTopPanel

  1  # GNUmed 
  2   
  3  #=========================================================== 
  4  __author__  = "R.Terry <rterry@gnumed.net>, I.Haywood <i.haywood@ugrad.unimelb.edu.au>, K.Hilbert <Karsten.Hilbert@gmx.net>" 
  5  __license__ = "GPL v2 or later" 
  6   
  7   
  8  import sys 
  9  import os.path 
 10  import datetime as pyDT 
 11  import logging 
 12  import decimal 
 13   
 14   
 15  import wx 
 16   
 17   
 18  from Gnumed.pycommon import gmGuiBroker 
 19  from Gnumed.pycommon import gmDispatcher 
 20  from Gnumed.pycommon import gmTools 
 21  from Gnumed.pycommon import gmCfg 
 22  from Gnumed.pycommon import gmCfg2 
 23  from Gnumed.pycommon import gmDateTime 
 24  from Gnumed.pycommon import gmI18N 
 25  from Gnumed.pycommon import gmExceptions 
 26   
 27  from Gnumed.business import gmPerson 
 28  from Gnumed.business import gmEMRStructItems 
 29  from Gnumed.business import gmAllergy 
 30  from Gnumed.business import gmLOINC 
 31  from Gnumed.business import gmClinicalCalculator 
 32  from Gnumed.business import gmPathLab 
 33  from Gnumed.business import gmPraxis 
 34   
 35  from Gnumed.wxpython import gmGuiHelpers 
 36  from Gnumed.wxpython import gmDemographicsWidgets 
 37  from Gnumed.wxpython import gmAllergyWidgets 
 38  from Gnumed.wxpython import gmPatSearchWidgets 
 39  from Gnumed.wxpython import gmEMRStructWidgets 
 40  from Gnumed.wxpython import gmPatPicWidgets 
 41   
 42   
 43  _log = logging.getLogger('gm.ui') 
 44   
 45  #=========================================================== 
 46  from Gnumed.wxGladeWidgets import wxgTopPnl 
 47   
48 -class cTopPnl(wxgTopPnl.wxgTopPnl):
49
50 - def __init__(self, *args, **kwargs):
51 52 wxgTopPnl.wxgTopPnl.__init__(self, *args, **kwargs) 53 54 self.__gb = gmGuiBroker.GuiBroker() 55 56 self.curr_pat = gmPerson.gmCurrentPatient() 57 58 self.__init_ui() 59 self.__register_interests()
60 61 #-------------------------------------------------------
62 - def __init_ui(self):
63 cfg = gmCfg2.gmCfgData() 64 if cfg.get(option = 'slave'): 65 self._TCTRL_patient_selector.SetEditable(0) 66 self._TCTRL_patient_selector.SetToolTip(None) 67 68 if sys.platform == 'darwin': 69 _log.debug('adjusting font size on Mac for top panel parts') 70 for ctrl in [self._TCTRL_patient_selector, self._LBL_age, self._LBL_allergies, self._TCTRL_allergies]: 71 curr_font = ctrl.GetFont() 72 mac_font = wx.Font(curr_font.GetNativeFontInfo()) 73 mac_font.SetPointSize(pointSize = int(curr_font.GetPointSize() / 0.8)) 74 ctrl.SetFont(mac_font) 75 76 self.__lab_panel = self.__get_lab_panel()
77 78 #-------------------------------------------------------
79 - def __register_interests(self):
80 # events 81 self._TCTRL_allergies.Bind(wx.EVT_LEFT_DCLICK, self._on_allergies_dclicked) 82 83 # client internal signals 84 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 85 gmDispatcher.connect(signal = 'focus_patient_search', receiver = self._on_focus_patient_search) 86 87 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
88 89 #---------------------------------------------- 90 # event handling 91 #----------------------------------------------
92 - def _on_allergies_dclicked(self, evt):
93 if not self.curr_pat.connected: 94 gmDispatcher.send('statustext', msg = _('Cannot activate Allergy Manager. No active patient.')) 95 return 96 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 97 dlg.ShowModal() 98 return
99 100 #----------------------------------------------
101 - def _on_database_signal(self, **kwds):
102 103 if kwds['table'] not in ['dem.identity', 'dem.names', 'dem.identity_tag', 'clin.allergy', 'clin.allergy_state', 'clin.test_result', 'clin.patient']: 104 return True 105 106 if self.curr_pat.connected: 107 # signal is not about our patient: ignore signal 108 if kwds['pk_identity'] != self.curr_pat.ID: 109 return True 110 111 if kwds['table'] == 'dem.identity': 112 # we don't care about newly INSERTed or DELETEd patients 113 if kwds['operation'] != 'UPDATE': 114 return True 115 self.__update_age_label() 116 return True 117 118 if kwds['table'] == 'dem.names': 119 self.__update_age_label() 120 return True 121 122 if kwds['table'] == 'dem.identity_tag': 123 self.__update_tags() 124 return True 125 126 if kwds['table'] in ['clin.allergy', 'clin.allergy_state']: 127 self.__update_allergies() 128 return True 129 130 if kwds['table'] in ['clin.test_result', 'clin.patient']: 131 self.__update_lab() 132 return True 133 134 return True
135 136 #----------------------------------------------
137 - def _on_post_patient_selection(self, **kwargs):
138 wx.CallAfter(self.__on_post_patient_selection)
139 140 #-------------------------------------------------------
141 - def __on_post_patient_selection(self):
142 self.__update_age_label() 143 self.__update_allergies() 144 self.__update_tags() 145 self.__update_lab() 146 self.Layout()
147 148 #-------------------------------------------------------
149 - def _on_focus_patient_search(self, **kwargs):
150 wx.CallAfter(self._TCTRL_patient_selector.SetFocus)
151 152 #------------------------------------------------------- 153 # internal API 154 #-------------------------------------------------------
155 - def __get_lab_panel(self):
156 # get panel to use 157 dbcfg = gmCfg.cCfgSQL() 158 pk_panel = dbcfg.get2 ( 159 option = u'horstspace.top_panel.lab_panel', 160 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 161 bias = 'user' 162 ) 163 if pk_panel is None: 164 return None 165 166 try: 167 panel = gmPathLab.cTestPanel(aPK_obj = pk_panel) 168 except gmExceptions.ConstructorError: 169 _log.exception('cannot load configured test panel') 170 panel = None 171 if panel is not None: 172 return panel 173 174 _log.error('Cannot load test panel [#%s] configured for patient pane (horstspace.top_panel.lab_panel).', pk_panel) 175 gmGuiHelpers.gm_show_error ( 176 title = _('GNUmed startup'), 177 error = _( 178 'Cannot load test panel [#%s] configured\n' 179 'for the top pane with option\n' 180 '\n' 181 ' <horstspace.top_panel.lab_panel>\n' 182 '\n' 183 'Please reconfigure.' 184 ) % pk_panel 185 ) 186 return None
187 188 #-------------------------------------------------------
189 - def __update_tags(self):
190 self._PNL_tags.refresh(patient = self.curr_pat)
191 192 #-------------------------------------------------------
193 - def __update_lab(self):
194 195 if not self.curr_pat.connected: 196 self._LBL_lab.SetLabel('') 197 return 198 199 tests2show = [] 200 201 rrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_rr_quantity, max_no_of_results = 1) 202 if len(rrs) == 0: 203 tests2show.append(_('RR ?')) 204 else: 205 tests2show.append(rrs[0]['unified_val']) 206 207 hrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_heart_rate_quantity, max_no_of_results = 1) 208 if len(hrs) > 0: 209 tests2show.append('%s %s' % (hrs[0]['abbrev_tt'], hrs[0]['unified_val'])) 210 211 bmi = self.curr_pat.emr.bmi 212 if bmi.numeric_value is not None: 213 tests2show.append(_('BMI %s') % bmi.numeric_value.quantize(decimal.Decimal('1.'))) 214 else: 215 weights = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_weight, max_no_of_results = 1) 216 if len(weights) == 0: 217 tests2show.append(_('BMI ?')) 218 else: 219 tests2show.append('%s%s' % (weights[0]['unified_val'], weights[0]['val_unit'])) 220 221 gfr_or_crea = self.curr_pat.emr.best_gfr_or_crea 222 if gfr_or_crea is None: 223 tests2show.append(_('GFR ?')) 224 else: 225 try: 226 tests2show.append(_('GFR %s') % gfr_or_crea.numeric_value.quantize(decimal.Decimal('1.'))) 227 except AttributeError: 228 tests2show.append('%s %s' % (gfr_or_crea['abbrev_tt'], gfr_or_crea['unified_val'])) 229 230 edc = self.curr_pat.emr.EDC 231 if edc is not None: 232 if self.curr_pat.emr.EDC_is_fishy: 233 tests2show.append(_('?EDC %s') % gmDateTime.pydt_strftime(edc, '%Y-%b-%d', accuracy = gmDateTime.acc_days)) 234 else: 235 tests2show.append(_('EDC %s') % gmDateTime.pydt_strftime(edc, '%Y-%b-%d', accuracy = gmDateTime.acc_days)) 236 237 inrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_inr_quantity, max_no_of_results = 1) 238 if len(inrs) > 0: 239 tests2show.append('%s %s' % (inrs[0]['abbrev_tt'], inrs[0]['unified_val'])) 240 241 # include panel if configured, only show if exist 242 if self.__lab_panel is not None: 243 for result in self.__lab_panel.get_most_recent_results(pk_patient = self.curr_pat.ID, order_by = 'unified_abbrev', group_by_meta_type = True): 244 tests2show.append('%s %s' % (result['abbrev_tt'], result['unified_val'])) 245 246 self._LBL_lab.SetLabel('; '.join(tests2show))
247 248 #-------------------------------------------------------
249 - def __update_age_label(self):
250 251 # no patient 252 if not self.curr_pat.connected: 253 self._LBL_age.SetLabel(_('<Age>')) 254 self._LBL_age.SetToolTip(_('no patient selected')) 255 return 256 257 # gender is always known 258 tt = _('Gender: %s (%s) - %s\n') % ( 259 self.curr_pat.gender_symbol, 260 gmTools.coalesce(self.curr_pat['gender'], '?'), 261 self.curr_pat.gender_string 262 ) 263 264 # dob is not known 265 if self.curr_pat['dob'] is None: 266 age = '%s %s' % ( 267 self.curr_pat.gender_symbol, 268 self.curr_pat.get_formatted_dob() 269 ) 270 self._LBL_age.SetLabel(age) 271 self._LBL_age.SetToolTip(tt) 272 return 273 274 tt += _('Born: %s\n') % self.curr_pat.get_formatted_dob(format = '%d %b %Y') 275 276 # patient is dead 277 if self.curr_pat['deceased'] is not None: 278 tt += _('Died: %s\n') % gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y') 279 tt += _('At age: %s\n') % self.curr_pat['medical_age'] 280 age = '%s %s - %s (%s)' % ( 281 self.curr_pat.gender_symbol, 282 self.curr_pat.get_formatted_dob(format = '%d %b %Y'), 283 gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y'), 284 self.curr_pat['medical_age'] 285 ) 286 if self.curr_pat['dob_is_estimated']: 287 tt += _(' (date of birth and age are estimated)\n') 288 self._LBL_age.SetLabel(age) 289 self._LBL_age.SetToolTip(tt) 290 return 291 292 # patient alive 293 now = gmDateTime.pydt_now_here() 294 295 # patient birthday ? 296 if self.curr_pat.get_formatted_dob(format = '%m-%d', honor_estimation = False) == now.strftime('%m-%d'): 297 template = _('%(sex)s %(dob)s (%(age)s today !)') 298 tt += _("\nToday is the patient's birthday !\n\n") 299 tt += _('Age: %s\n') % self.curr_pat['medical_age'] 300 else: 301 tt += _('Age: %s, birthday:\n') % self.curr_pat['medical_age'] 302 if self.curr_pat.current_birthday_passed is True: 303 template = '%(sex)s %(dob)s%(l_arr)s (%(age)s)' 304 tt += ' ' + _('last: %s ago (this year)') % gmDateTime.format_apparent_age_medically ( 305 age = gmDateTime.calculate_apparent_age(start = self.curr_pat.birthday_this_year, end = now) 306 ) + '\n' 307 tt += ' ' + _('next: in %s (next year)') % gmDateTime.format_apparent_age_medically ( 308 age = gmDateTime.calculate_apparent_age(start = now, end = self.curr_pat.birthday_next_year) 309 ) + '\n' 310 elif self.curr_pat.current_birthday_passed is False: 311 template = '%(sex)s %(r_arr)s%(dob)s (%(age)s)' 312 tt += ' ' + _('next: in %s (this year)') % gmDateTime.format_apparent_age_medically ( 313 age = gmDateTime.calculate_apparent_age(start = now, end = self.curr_pat.birthday_this_year) 314 ) + '\n' 315 tt += ' ' + _('last: %s ago (last year)') % gmDateTime.format_apparent_age_medically ( 316 age = gmDateTime.calculate_apparent_age(start = self.curr_pat.birthday_last_year, end = now) 317 ) + '\n' 318 else: # None, unknown 319 template = '%(sex)s %(dob)s (%(age)s)' 320 321 # FIXME: if the age is below, say, 2 hours we should fire 322 # a timer here that updates the age in increments of 1 minute ... :-) 323 age = template % { 324 'sex': self.curr_pat.gender_symbol, 325 'dob': self.curr_pat.get_formatted_dob(format = '%d %b %Y'), 326 'age': self.curr_pat['medical_age'], 327 'r_arr': gmTools.u_arrow2right, 328 'l_arr': gmTools.u_left_arrow 329 } 330 331 # Easter Egg ;-) 332 if self.curr_pat['lastnames'] == 'Leibner': 333 if self.curr_pat['firstnames'] == 'Steffi': 334 if self.curr_pat['preferred'] == 'Wildfang': 335 age = '%s %s' % (gmTools.u_black_heart, age) 336 337 if self.curr_pat['dob_is_estimated']: 338 tt += _(' (date of birth and age are estimated)\n') 339 340 self._LBL_age.SetLabel(age) 341 self._LBL_age.SetToolTip(tt)
342 343 #-------------------------------------------------------
344 - def __update_allergies(self, **kwargs):
345 346 if not self.curr_pat.connected: 347 self._LBL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 348 self._TCTRL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 349 self._TCTRL_allergies.SetValue('') 350 self._TCTRL_allergies.SetToolTip('') 351 return 352 353 show_red = True 354 355 emr = self.curr_pat.emr 356 state = emr.allergy_state 357 358 # state in tooltip 359 if state['last_confirmed'] is None: 360 confirmed = _('never') 361 else: 362 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d') 363 tt = (state.state_string + (90 * ' '))[:90] + '\n' 364 tt += _('last confirmed %s\n') % confirmed 365 tt += gmTools.coalesce(state['comment'], '', _('Comment (%s): %%s') % state['modified_by']) 366 tt += '\n' 367 368 # allergies 369 display = [] 370 for allergy in emr.get_allergies(): 371 # in field: "true" allergies only, not intolerances 372 if allergy['type'] == 'allergy': 373 display.append(allergy['descriptor'][:10].strip() + gmTools.u_ellipsis) 374 # in tooltip 375 if allergy['definite']: 376 certainty = _('definite') 377 else: 378 certainty = _('suspected') 379 reaction = gmTools.coalesce(allergy['reaction'], _('reaction not recorded')) 380 if len(reaction) > 50: 381 reaction = reaction[:50] + gmTools.u_ellipsis 382 tt += '%s (%s, %s): %s\n' % ( 383 allergy['descriptor'], 384 allergy['l10n_type'], 385 certainty, 386 reaction 387 ) 388 389 if len(display) == 0: 390 display = state.state_symbol 391 if display == gmTools.u_diameter: 392 show_red = False 393 else: 394 display = ','.join(display) 395 396 if state['last_confirmed'] is not None: 397 display += gmDateTime.pydt_strftime(state['last_confirmed'], ' (%Y %b)') 398 399 if show_red: 400 self._LBL_allergies.SetForegroundColour('red') 401 self._TCTRL_allergies.SetForegroundColour('red') 402 else: 403 self._LBL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 404 self._TCTRL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) 405 406 self._TCTRL_allergies.SetValue(display) 407 self._TCTRL_allergies.SetToolTip(tt)
408 409 #=========================================================== 410 if __name__ == "__main__": 411 app = wxPyWidgetTester(size = (400, 200)) 412 app.SetWidget(cMainTopPanel, -1) 413 app.SetWidget(cTopPanel, -1) 414 app.MainLoop() 415 #=========================================================== 416