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

Source Code for Module Gnumed.wxpython.gmMedicationWidgets

   1  """GNUmed medication/substances handling widgets. 
   2  """ 
   3  #================================================================ 
   4  __version__ = "$Revision: 1.33 $" 
   5  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
   6   
   7  import logging, sys, os.path, webbrowser 
   8   
   9   
  10  import wx, wx.grid 
  11   
  12   
  13  if __name__ == '__main__': 
  14          sys.path.insert(0, '../../') 
  15  from Gnumed.pycommon import gmDispatcher, gmCfg, gmShellAPI, gmTools, gmDateTime 
  16  from Gnumed.pycommon import gmMatchProvider, gmI18N, gmPrinting, gmCfg2 
  17  from Gnumed.business import gmPerson, gmATC, gmSurgery, gmMedication, gmForms 
  18  from Gnumed.wxpython import gmGuiHelpers, gmRegetMixin, gmAuthWidgets, gmEditArea, gmMacro 
  19  from Gnumed.wxpython import gmCfgWidgets, gmListWidgets, gmPhraseWheel, gmFormWidgets 
  20  from Gnumed.wxpython import gmAllergyWidgets 
  21   
  22   
  23  _log = logging.getLogger('gm.ui') 
  24  _log.info(__version__) 
  25  #============================================================ 
26 -def browse_atc_reference(parent=None):
27 28 if parent is None: 29 parent = wx.GetApp().GetTopWindow() 30 #------------------------------------------------------------ 31 def refresh(lctrl): 32 atcs = gmATC.get_reference_atcs() 33 34 items = [ [ 35 a['atc'], 36 a['term'], 37 u'%s' % gmTools.coalesce(a['ddd'], u''), 38 gmTools.coalesce(a['unit'], u''), 39 gmTools.coalesce(a['administrative_route'], u''), 40 gmTools.coalesce(a['comment'], u''), 41 a['version'], 42 a['lang'] 43 ] for a in atcs ] 44 lctrl.set_string_items(items) 45 lctrl.set_data(atcs)
46 #------------------------------------------------------------ 47 gmListWidgets.get_choices_from_list ( 48 parent = parent, 49 msg = _('\nThe ATC codes as known to GNUmed.\n'), 50 caption = _('Showing ATC codes.'), 51 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ], 52 single_selection = True, 53 refresh_callback = refresh 54 ) 55 56 #============================================================
57 -def manage_substances_in_brands(parent=None):
58 59 if parent is None: 60 parent = wx.GetApp().GetTopWindow() 61 62 #------------------------------------------------------------ 63 def delete(component): 64 gmMedication.delete_component_from_branded_drug ( 65 brand = component['pk_brand'], 66 component = component['pk_substance_in_brand'] 67 ) 68 return True
69 #------------------------------------------------------------ 70 def refresh(lctrl): 71 substs = gmMedication.get_substances_in_brands() 72 items = [ [ 73 u'%s%s' % (s['brand'], gmTools.coalesce(s['atc_brand'], u'', u' (%s)')), 74 s['substance'], 75 gmTools.coalesce(s['atc_substance'], u''), 76 s['preparation'], 77 gmTools.coalesce(s['external_code_brand'], u'', u'%%s [%s]' % s['external_code_type_brand']), 78 s['pk_substance_in_brand'] 79 ] for s in substs ] 80 lctrl.set_string_items(items) 81 lctrl.set_data(substs) 82 #------------------------------------------------------------ 83 msg = _('\nThese are the substances in the drug brands known to GNUmed.\n') 84 85 gmListWidgets.get_choices_from_list ( 86 parent = parent, 87 msg = msg, 88 caption = _('Showing drug brand components (substances).'), 89 columns = [_('Brand'), _('Substance'), u'ATC', _('Preparation'), _('Code'), u'#'], 90 single_selection = True, 91 #new_callback = new, 92 #edit_callback = edit, 93 delete_callback = delete, 94 refresh_callback = refresh 95 ) 96 #============================================================
97 -def manage_branded_drugs(parent=None):
98 99 if parent is None: 100 parent = wx.GetApp().GetTopWindow() 101 #------------------------------------------------------------ 102 def delete(brand): 103 gmMedication.delete_branded_drug(brand = brand['pk']) 104 return True
105 #------------------------------------------------------------ 106 def new(): 107 drug_db = get_drug_database(parent = parent) 108 109 if drug_db is None: 110 return False 111 112 drug_db.import_drugs() 113 114 return True 115 #------------------------------------------------------------ 116 def refresh(lctrl): 117 drugs = gmMedication.get_branded_drugs() 118 items = [ [ 119 d['description'], 120 d['preparation'], 121 gmTools.coalesce(d['atc_code'], u''), 122 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']), 123 d['pk'] 124 ] for d in drugs ] 125 lctrl.set_string_items(items) 126 lctrl.set_data(drugs) 127 #------------------------------------------------------------ 128 msg = _('\nThese are the drug brands known to GNUmed.\n') 129 130 gmListWidgets.get_choices_from_list ( 131 parent = parent, 132 msg = msg, 133 caption = _('Showing branded drugs.'), 134 columns = [_('Name'), _('Preparation'), _('ATC'), _('Code'), u'#'], 135 single_selection = True, 136 refresh_callback = refresh, 137 new_callback = new, 138 #edit_callback = edit, 139 delete_callback = delete 140 ) 141 #============================================================
142 -def manage_substances_in_use(parent=None):
143 144 if parent is None: 145 parent = wx.GetApp().GetTopWindow() 146 #------------------------------------------------------------ 147 def delete(substance): 148 gmMedication.delete_used_substance(substance = substance['pk']) 149 return True
150 #------------------------------------------------------------ 151 def new(): 152 drug_db = get_drug_database(parent = parent) 153 154 if drug_db is None: 155 return False 156 157 drug_db.import_drugs() 158 159 return True 160 #------------------------------------------------------------ 161 def refresh(lctrl): 162 substs = gmMedication.get_substances_in_use() 163 items = [ [ 164 s['description'], 165 gmTools.coalesce(s['atc_code'], u''), 166 s['pk'] 167 ] for s in substs ] 168 lctrl.set_string_items(items) 169 lctrl.set_data(substs) 170 #------------------------------------------------------------ 171 msg = _('\nThese are the substances currently or previously\nconsumed across all patients.\n') 172 173 gmListWidgets.get_choices_from_list ( 174 parent = parent, 175 msg = msg, 176 caption = _('Showing consumed substances.'), 177 columns = [_('Name'), _('ATC'), u'#'], 178 single_selection = True, 179 refresh_callback = refresh, 180 new_callback = new, 181 #edit_callback = edit, 182 delete_callback = delete 183 ) 184 #============================================================ 185 # generic drug database access 186 #============================================================
187 -def configure_drug_data_source(parent=None):
188 gmCfgWidgets.configure_string_from_list_option ( 189 parent = parent, 190 message = _( 191 '\n' 192 'Please select the default drug data source from the list below.\n' 193 '\n' 194 'Note that to actually use it you need to have the database installed, too.' 195 ), 196 option = 'external.drug_data.default_source', 197 bias = 'user', 198 default_value = None, 199 choices = gmMedication.drug_data_source_interfaces.keys(), 200 columns = [_('Drug data source')], 201 data = gmMedication.drug_data_source_interfaces.keys(), 202 caption = _('Configuring default drug data source') 203 )
204 #============================================================
205 -def get_drug_database(parent = None):
206 dbcfg = gmCfg.cCfgSQL() 207 208 default_db = dbcfg.get2 ( 209 option = 'external.drug_data.default_source', 210 workplace = gmSurgery.gmCurrentPractice().active_workplace, 211 bias = 'workplace' 212 ) 213 214 if default_db is None: 215 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True) 216 configure_drug_data_source(parent = parent) 217 default_db = dbcfg.get2 ( 218 option = 'external.drug_data.default_source', 219 workplace = gmSurgery.gmCurrentPractice().active_workplace, 220 bias = 'workplace' 221 ) 222 if default_db is None: 223 gmGuiHelpers.gm_show_error ( 224 aMessage = _('There is no default drug database configured.'), 225 aTitle = _('Jumping to drug database') 226 ) 227 return None 228 229 try: 230 return gmMedication.drug_data_source_interfaces[default_db]() 231 except KeyError: 232 _log.error('faulty default drug data source configuration: %s', default_db) 233 return None
234 #============================================================
235 -def jump_to_drug_database():
236 dbcfg = gmCfg.cCfgSQL() 237 drug_db = get_drug_database() 238 if drug_db is None: 239 return 240 pat = gmPerson.gmCurrentPatient() 241 if pat.connected: 242 drug_db.patient = pat 243 drug_db.switch_to_frontend(blocking = False)
244 #============================================================
245 -def jump_to_ifap(import_drugs=False):
246 247 dbcfg = gmCfg.cCfgSQL() 248 249 ifap_cmd = dbcfg.get2 ( 250 option = 'external.ifap-win.shell_command', 251 workplace = gmSurgery.gmCurrentPractice().active_workplace, 252 bias = 'workplace', 253 default = 'wine "C:\Ifapwin\WIAMDB.EXE"' 254 ) 255 found, binary = gmShellAPI.detect_external_binary(ifap_cmd) 256 if not found: 257 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd) 258 return False 259 ifap_cmd = binary 260 261 if import_drugs: 262 transfer_file = os.path.expanduser(dbcfg.get2 ( 263 option = 'external.ifap-win.transfer_file', 264 workplace = gmSurgery.gmCurrentPractice().active_workplace, 265 bias = 'workplace', 266 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv' 267 )) 268 # file must exist for Ifap to write into it 269 try: 270 f = open(transfer_file, 'w+b').close() 271 except IOError: 272 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file) 273 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file) 274 return False 275 276 wx.BeginBusyCursor() 277 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs) 278 wx.EndBusyCursor() 279 280 if import_drugs: 281 # COMMENT: this file must exist PRIOR to invoking IFAP 282 # COMMENT: or else IFAP will not write data into it ... 283 try: 284 csv_file = open(transfer_file, 'rb') # FIXME: encoding 285 except: 286 _log.exception('cannot access [%s]', fname) 287 csv_file = None 288 289 if csv_file is not None: 290 import csv 291 csv_lines = csv.DictReader ( 292 csv_file, 293 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(), 294 delimiter = ';' 295 ) 296 pat = gmPerson.gmCurrentPatient() 297 emr = pat.get_emr() 298 # dummy episode for now 299 epi = emr.add_episode(episode_name = _('Current medication')) 300 for line in csv_lines: 301 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 302 line['Packungszahl'].strip(), 303 line['Handelsname'].strip(), 304 line['Form'].strip(), 305 line[u'Packungsgr\xf6\xdfe'].strip(), 306 line['Abpackungsmenge'].strip(), 307 line['Einheit'].strip(), 308 line['Hersteller'].strip(), 309 line['PZN'].strip() 310 ) 311 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi) 312 csv_file.close() 313 314 return True
315 #============================================================
316 -def update_atc_reference_data():
317 318 dlg = wx.FileDialog ( 319 parent = None, 320 message = _('Choose an ATC import config file'), 321 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')), 322 defaultFile = '', 323 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')), 324 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST 325 ) 326 327 result = dlg.ShowModal() 328 if result == wx.ID_CANCEL: 329 return 330 331 cfg_file = dlg.GetPath() 332 dlg.Destroy() 333 334 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data')) 335 if conn is None: 336 return False 337 338 wx.BeginBusyCursor() 339 340 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn): 341 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.')) 342 else: 343 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True) 344 345 wx.EndBusyCursor() 346 return True
347 348 #============================================================ 349 # current substance intake handling 350 #============================================================
351 -class cSubstanceSchedulePhraseWheel(gmPhraseWheel.cPhraseWheel):
352
353 - def __init__(self, *args, **kwargs):
354 355 query = u""" 356 SELECT schedule as sched, schedule 357 FROM clin.substance_intake 358 where schedule %(fragment_condition)s 359 ORDER BY sched 360 LIMIT 50""" 361 362 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 363 mp.setThresholds(1, 2, 4) 364 mp.word_separators = '[ \t=+&:@]+' 365 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 366 self.SetToolTipString(_('The schedule for taking this substance.')) 367 self.matcher = mp 368 self.selection_only = False
369 #============================================================
370 -class cSubstancePreparationPhraseWheel(gmPhraseWheel.cPhraseWheel):
371
372 - def __init__(self, *args, **kwargs):
373 374 query = u""" 375 ( 376 SELECT preparation as prep, preparation 377 FROM ref.branded_drug 378 where preparation %(fragment_condition)s 379 ) union ( 380 SELECT preparation as prep, preparation 381 FROM clin.substance_intake 382 where preparation %(fragment_condition)s 383 ) 384 order by prep 385 limit 30""" 386 387 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 388 mp.setThresholds(1, 2, 4) 389 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 390 self.SetToolTipString(_('The preparation (form) of the substance the patient is taking.')) 391 self.matcher = mp 392 self.selection_only = False
393 #============================================================
394 -class cSubstancePhraseWheel(gmPhraseWheel.cPhraseWheel):
395
396 - def __init__(self, *args, **kwargs):
397 398 query = u""" 399 ( 400 SELECT pk, (coalesce(atc_code || ': ', '') || description) as subst 401 FROM clin.consumed_substance 402 WHERE description %(fragment_condition)s 403 ) union ( 404 SELECT NULL, (coalesce(atc_code || ': ', '') || description) as subst 405 FROM ref.substance_in_brand 406 WHERE description %(fragment_condition)s 407 ) union ( 408 SELECT NULL, (atc || ': ' || term) as subst 409 FROM ref.v_atc 410 WHERE 411 is_group_code IS FALSE 412 AND 413 term %(fragment_condition)s 414 ) 415 order by subst 416 limit 50""" 417 418 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 419 mp.setThresholds(1, 2, 4) 420 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 421 self.SetToolTipString(_('The INN / substance the patient is taking.')) 422 self.matcher = mp 423 self.selection_only = False
424 #============================================================
425 -class cBrandedDrugPhraseWheel(gmPhraseWheel.cPhraseWheel):
426
427 - def __init__(self, *args, **kwargs):
428 429 query = u""" 430 SELECT pk, (coalesce(atc_code || ': ', '') || description || ' (' || preparation || ')') as brand 431 FROM ref.branded_drug 432 WHERE description %(fragment_condition)s 433 ORDER BY brand 434 LIMIT 50""" 435 436 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 437 mp.setThresholds(2, 3, 4) 438 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 439 self.SetToolTipString(_('The brand name of the drug the patient is taking.')) 440 self.matcher = mp 441 self.selection_only = False
442 443 #============================================================ 444 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl 445
446 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
447
448 - def __init__(self, *args, **kwargs):
449 450 try: 451 data = kwargs['substance'] 452 del kwargs['substance'] 453 except KeyError: 454 data = None 455 456 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs) 457 gmEditArea.cGenericEditAreaMixin.__init__(self) 458 self.mode = 'new' 459 self.data = data 460 if data is not None: 461 self.mode = 'edit' 462 463 self.__init_ui()
464 #----------------------------------------------------------------
465 - def __init_ui(self):
466 467 # adjust phrasewheels 468 469 self._PRW_brand.add_callback_on_lose_focus(callback = self._on_leave_brand)
470 #----------------------------------------------------------------
471 - def __refresh_allergies(self):
472 emr = gmPerson.gmCurrentPatient().get_emr() 473 474 state = emr.allergy_state 475 if state['last_confirmed'] is None: 476 confirmed = _('never') 477 else: 478 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding()) 479 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed) 480 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by']) 481 msg += u'\n' 482 483 for allergy in emr.get_allergies(): 484 msg += u'%s (%s, %s): %s\n' % ( 485 allergy['descriptor'], 486 allergy['l10n_type'], 487 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'), 488 gmTools.coalesce(allergy['reaction'], _('reaction not recorded')) 489 ) 490 491 self._LBL_allergies.SetLabel(msg)
492 #----------------------------------------------------------------
494 495 if self._PRW_brand.GetData() is None: 496 self._TCTRL_brand_ingredients.SetValue(u'') 497 if self.data is None: 498 return 499 if self.data['pk_brand'] is None: 500 return 501 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand']) 502 503 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData()) 504 505 if self.data is None: 506 self._PRW_preparation.SetText(brand['preparation'], None) 507 else: 508 self._PRW_preparation.SetText ( 509 gmTools.coalesce(self.data['preparation'], brand['preparation']), 510 self.data['preparation'] 511 ) 512 513 comps = brand.components 514 515 if comps is None: 516 return 517 518 if len(comps) == 0: 519 return 520 521 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ]) 522 self._TCTRL_brand_ingredients.SetValue(comps)
523 #---------------------------------------------------------------- 524 # generic Edit Area mixin API 525 #----------------------------------------------------------------
526 - def _valid_for_save(self):
527 528 validity = True 529 530 if self._PRW_substance.GetValue().strip() == u'': 531 self._PRW_substance.display_as_valid(False) 532 validity = False 533 else: 534 self._PRW_substance.display_as_valid(True) 535 536 if self._PRW_preparation.GetValue().strip() == u'': 537 self._PRW_preparation.display_as_valid(False) 538 validity = False 539 else: 540 self._PRW_preparation.display_as_valid(True) 541 542 if self._CHBOX_approved.IsChecked(): 543 if self._PRW_episode.GetValue().strip() == u'': 544 self._PRW_episode.display_as_valid(False) 545 validity = False 546 else: 547 self._PRW_episode.display_as_valid(True) 548 549 if self._CHBOX_approved.IsChecked() is True: 550 self._PRW_duration.display_as_valid(True) 551 else: 552 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 553 self._PRW_duration.display_as_valid(True) 554 else: 555 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None: 556 self._PRW_duration.display_as_valid(False) 557 validity = False 558 else: 559 self._PRW_duration.display_as_valid(True) 560 561 end = self._DP_discontinued.GetValue(as_pydt = True) 562 if end is not None: 563 start = self._DP_started.GetValue(as_pydt = True) 564 if start > end: 565 self._DP_started.display_as_valid(False) 566 self._DP_discontinued.display_as_valid(False) 567 validity = False 568 else: 569 self._DP_started.display_as_valid(True) 570 self._DP_discontinued.display_as_valid(True) 571 572 if validity is False: 573 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.')) 574 575 return validity
576 #----------------------------------------------------------------
577 - def _save_as_new(self):
578 579 emr = gmPerson.gmCurrentPatient().get_emr() 580 581 # 1) create substance intake entry 582 if self._PRW_substance.GetData() is None: 583 subst = self._PRW_substance.GetValue().strip() 584 else: 585 # normalize, do not simply re-use name from phrasewheel 586 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())['description'] 587 588 intake = emr.add_substance_intake ( 589 substance = subst, 590 episode = self._PRW_episode.GetData(can_create = True), 591 preparation = self._PRW_preparation.GetValue() 592 ) 593 594 intake['strength'] = self._PRW_strength.GetValue() 595 intake['started'] = self._DP_started.GetValue(as_pydt = True) 596 intake['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True) 597 if intake['discontinued'] is None: 598 intake['discontinue_reason'] = None 599 else: 600 intake['discontinue_reason'] = self._PRW_discontinue_reason().GetValue().strip() 601 intake['schedule'] = self._PRW_schedule.GetValue() 602 intake['aim'] = self._PRW_aim.GetValue() 603 intake['notes'] = self._PRW_notes.GetValue() 604 intake['is_long_term'] = self._CHBOX_long_term.IsChecked() 605 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 606 607 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 608 intake['duration'] = None 609 else: 610 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 611 612 # 2) create or retrieve brand 613 brand = None 614 pk_brand = self._PRW_brand.GetData() 615 616 # brand pre-selected ? 617 if pk_brand is None: 618 # no, so ... 619 desc = self._PRW_brand.GetValue().strip() 620 if desc != u'': 621 # ... create or get it 622 brand = gmMedication.create_branded_drug ( 623 brand_name = desc, 624 preparation = self._PRW_preparation.GetValue().strip(), 625 return_existing = True 626 ) 627 pk_brand = brand['pk'] 628 else: 629 # yes, so get it 630 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand) 631 632 # 3) link brand, if available 633 intake['pk_brand'] = pk_brand 634 intake.save() 635 636 # brand neither creatable nor pre-selected 637 if brand is None: 638 self.data = intake 639 return True 640 641 # 4) add substance to brand as component (because 642 # that's effectively what we are saying here) 643 # FIXME: we may want to ask the user here 644 # FIXME: or only do it if there are no components yet 645 if self._PRW_substance.GetData() is None: 646 brand.add_component(substance = self._PRW_substance.GetValue().strip()) 647 else: 648 # normalize substance name 649 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData()) 650 if subst is not None: 651 brand.add_component(substance = subst['description']) 652 653 self.data = intake 654 655 if self._CHBOX_is_allergy.IsChecked(): 656 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 657 # open for editing 658 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 659 dlg.ShowModal() 660 661 return True
662 #----------------------------------------------------------------
663 - def _save_as_update(self):
664 665 if self._PRW_substance.GetData() is None: 666 self.data['pk_substance'] = gmMedication.create_used_substance ( 667 substance = self._PRW_substance.GetValue().strip() 668 )['pk'] 669 else: 670 self.data['pk_substance'] = self._PRW_substance.GetData() 671 672 self.data['started'] = self._DP_started.GetValue(as_pydt=True) 673 self.data['discontinued'] = self._DP_discontinued.GetValue(as_pydt=True) 674 if self.data['discontinued'] is None: 675 self.data['discontinue_reason'] = None 676 else: 677 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip() 678 self.data['preparation'] = self._PRW_preparation.GetValue() 679 self.data['strength'] = self._PRW_strength.GetValue() 680 self.data['schedule'] = self._PRW_schedule.GetValue() 681 self.data['aim'] = self._PRW_aim.GetValue() 682 self.data['notes'] = self._PRW_notes.GetValue() 683 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked() 684 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 685 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True) 686 687 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 688 self.data['duration'] = None 689 else: 690 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 691 692 if self._PRW_brand.GetData() is None: 693 desc = self._PRW_brand.GetValue().strip() 694 if desc != u'': 695 # create or get brand 696 self.data['pk_brand'] = gmMedication.create_branded_drug ( 697 brand_name = desc, 698 preparation = self._PRW_preparation.GetValue().strip(), 699 return_existing = True 700 )['pk'] 701 else: 702 self.data['pk_brand'] = self._PRW_brand.GetData() 703 704 self.data.save() 705 706 if self._CHBOX_is_allergy.IsChecked(): 707 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 708 # open for editing 709 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 710 dlg.ShowModal() 711 712 return True
713 #----------------------------------------------------------------
714 - def _refresh_as_new(self):
715 self._PRW_substance.SetText(u'', None) 716 self._PRW_strength.SetText(u'', None) 717 # self._PRW_preparation.SetText(u'', None) 718 self._PRW_schedule.SetText(u'', None) 719 self._PRW_duration.SetText(u'', None) 720 self._PRW_aim.SetText(u'', None) 721 self._PRW_notes.SetText(u'', None) 722 self._PRW_episode.SetData(None) 723 724 self._CHBOX_long_term.SetValue(False) 725 self._CHBOX_approved.SetValue(True) 726 727 self._DP_started.SetValue(gmDateTime.pydt_now_here()) 728 self._DP_discontinued.SetValue(None) 729 self._PRW_discontinue_reason.SetValue(u'') 730 731 self.__refresh_brand_and_components() 732 self.__refresh_allergies() 733 734 self._PRW_substance.SetFocus()
735 #----------------------------------------------------------------
736 - def _refresh_from_existing(self):
737 738 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance']) 739 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength']) 740 741 if self.data['is_long_term']: 742 self._CHBOX_long_term.SetValue(True) 743 self._PRW_duration.Enable(False) 744 self._PRW_duration.SetText(gmTools.u_infinity, None) 745 self._BTN_discontinued_as_planned.Enable(False) 746 else: 747 self._CHBOX_long_term.SetValue(False) 748 self._PRW_duration.Enable(True) 749 self._BTN_discontinued_as_planned.Enable(True) 750 if self.data['duration'] is None: 751 self._PRW_duration.SetText(u'', None) 752 else: 753 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration']) 754 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim']) 755 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes']) 756 self._PRW_episode.SetData(self.data['pk_episode']) 757 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule']) 758 759 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of']) 760 761 self._DP_started.SetValue(self.data['started']) 762 self._DP_discontinued.SetValue(self.data['discontinued']) 763 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u'')) 764 765 self.__refresh_brand_and_components() 766 self.__refresh_allergies() 767 768 self._PRW_substance.SetFocus()
769 #----------------------------------------------------------------
771 self._refresh_as_new() 772 773 self._PRW_substance.SetText(u'', None) 774 self._PRW_strength.SetText(u'', None) 775 self._PRW_notes.SetText(u'', None) 776 777 self.__refresh_brand_and_components() 778 self.__refresh_allergies() 779 780 self._PRW_substance.SetFocus()
781 #---------------------------------------------------------------- 782 # event handlers 783 #----------------------------------------------------------------
784 - def _on_leave_brand(self):
785 self.__refresh_brand_and_components()
786 #----------------------------------------------------------------
787 - def _on_discontinued_date_changed(self, event):
788 if self._DP_discontinued.GetValue() is None: 789 self._PRW_discontinue_reason.Enable(False) 790 self._CHBOX_is_allergy.Enable(False) 791 else: 792 self._PRW_discontinue_reason.Enable(True) 793 self._CHBOX_is_allergy.Enable(True)
794 #----------------------------------------------------------------
795 - def _on_get_substance_button_pressed(self, event):
796 drug_db = get_drug_database() 797 if drug_db is None: 798 return 799 800 result = drug_db.import_drugs() 801 if result is None: 802 return 803 804 new_drugs, new_substances = result 805 if len(new_substances) == 0: 806 return 807 808 # FIXME: could usefully 809 # FIXME: a) ask which to post-process 810 # FIXME: b) remember the others for post-processing 811 first = new_substances[0] 812 self._PRW_substance.SetText(first['description'], first['pk'])
813 #----------------------------------------------------------------
814 - def _on_get_brand_button_pressed(self, event):
815 drug_db = get_drug_database() 816 self.__refresh_allergies() 817 if drug_db is None: 818 return 819 820 result = drug_db.import_drugs() 821 self.__refresh_allergies() 822 if result is None: 823 return 824 825 new_drugs, new_substances = result 826 if len(new_drugs) == 0: 827 return 828 # FIXME: could usefully 829 # FIXME: a) ask which to post-process 830 # FIXME: b) remember the others for post-processing 831 first = new_drugs[0] 832 self._PRW_brand.SetText(first['description'], first['pk']) 833 834 self.__refresh_brand_and_components()
835 #----------------------------------------------------------------
837 838 now = gmDateTime.pydt_now_here() 839 840 self.__refresh_allergies() 841 842 # do we have a (full) plan ? 843 if None not in [self.data['started'], self.data['duration']]: 844 planned_end = self.data['started'] + self.data['duration'] 845 # the plan hasn't ended so [Per plan] can't apply ;-) 846 if planned_end > now: 847 return 848 self._DP_discontinued.SetValue(planned_end) 849 self._PRW_discontinue_reason.Enable(True) 850 self._PRW_discontinue_reason.SetValue(u'') 851 self._CHBOX_is_allergy.Enable(True) 852 return 853 854 # we know started but not duration: apparently the plan is to stop today 855 if self.data['started'] is not None: 856 # but we haven't started yet so we can't stop 857 if self.data['started'] > now: 858 return 859 860 self._DP_discontinued.SetValue(now) 861 self._PRW_discontinue_reason.Enable(True) 862 self._PRW_discontinue_reason.SetValue(u'') 863 self._CHBOX_is_allergy.Enable(True)
864 #----------------------------------------------------------------
865 - def _on_chbox_long_term_checked(self, event):
866 if self._CHBOX_long_term.IsChecked() is True: 867 self._PRW_duration.Enable(False) 868 self._BTN_discontinued_as_planned.Enable(False) 869 self._PRW_discontinue_reason.Enable(False) 870 self._CHBOX_is_allergy.Enable(False) 871 else: 872 self._PRW_duration.Enable(True) 873 self._BTN_discontinued_as_planned.Enable(True) 874 self._PRW_discontinue_reason.Enable(True) 875 self._CHBOX_is_allergy.Enable(True) 876 877 self.__refresh_allergies()
878 #----------------------------------------------------------------
879 - def _on_chbox_is_allergy_checked(self, event):
880 if self._CHBOX_is_allergy.IsChecked() is True: 881 val = self._PRW_discontinue_reason.GetValue().strip() 882 if not val.startswith(_('not tolerated:')): 883 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val)) 884 885 self.__refresh_allergies()
886 #============================================================
887 -def delete_substance_intake(parent=None, substance=None):
888 delete_it = gmGuiHelpers.gm_show_question ( 889 aMessage = _( 890 'Do you really want to remove this substance intake ?\n' 891 '\n' 892 'It may be prudent to edit the details first so as to\n' 893 'leave behind some indication of why it was deleted.\n' 894 ), 895 aTitle = _('Deleting medication / substance intake') 896 ) 897 if not delete_it: 898 return 899 900 gmMedication.delete_substance_intake(substance = substance)
901 #------------------------------------------------------------
902 -def edit_intake_of_substance(parent = None, substance=None):
903 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance) 904 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None)) 905 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake'))) 906 if dlg.ShowModal() == wx.ID_OK: 907 dlg.Destroy() 908 return True 909 dlg.Destroy() 910 return False
911 #============================================================ 912 # current substances grid 913 #------------------------------------------------------------
914 -def configure_medication_list_template(parent=None):
915 916 if parent is None: 917 parent = wx.GetApp().GetTopWindow() 918 919 template = gmFormWidgets.manage_form_templates(parent = parent) 920 option = u'form_templates.medication_list' 921 922 if template is None: 923 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 924 return None 925 926 if template['engine'] != u'L': 927 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 928 return None 929 930 dbcfg = gmCfg.cCfgSQL() 931 dbcfg.set ( 932 workplace = gmSurgery.gmCurrentPractice().active_workplace, 933 option = option, 934 value = u'%s - %s' % (template['name_long'], template['external_version']) 935 ) 936 937 return template
938 #------------------------------------------------------------ 1020 #------------------------------------------------------------
1021 -class cCurrentSubstancesGrid(wx.grid.Grid):
1022 """A grid class for displaying current substance intake. 1023 1024 - does NOT listen to the currently active patient 1025 - thereby it can display any patient at any time 1026 """
1027 - def __init__(self, *args, **kwargs):
1028 1029 wx.grid.Grid.__init__(self, *args, **kwargs) 1030 1031 self.__patient = None 1032 self.__row_data = {} 1033 self.__prev_row = None 1034 self.__prev_tooltip_row = None 1035 self.__prev_cell_0 = None 1036 self.__grouping_mode = u'episode' 1037 self.__filter_show_unapproved = False 1038 self.__filter_show_inactive = False 1039 1040 self.__grouping2col_labels = { 1041 u'episode': [ 1042 _('Episode'), 1043 _('Substance'), 1044 _('Dose'), 1045 _('Schedule'), 1046 _('Started'), 1047 _('Duration'), 1048 _('Brand') 1049 ], 1050 u'brand': [ 1051 _('Brand'), 1052 _('Schedule'), 1053 _('Substance'), 1054 _('Dose'), 1055 _('Started'), 1056 _('Duration'), 1057 _('Episode') 1058 ] 1059 } 1060 1061 self.__grouping2order_by_clauses = { 1062 u'episode': u'pk_health_issue nulls first, episode, substance, started', 1063 u'brand': u'brand nulls last, substance, started' 1064 } 1065 1066 self.__init_ui() 1067 self.__register_events()
1068 #------------------------------------------------------------ 1069 # external API 1070 #------------------------------------------------------------
1071 - def get_selected_cells(self):
1072 1073 sel_block_top_left = self.GetSelectionBlockTopLeft() 1074 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 1075 sel_cols = self.GetSelectedCols() 1076 sel_rows = self.GetSelectedRows() 1077 1078 selected_cells = [] 1079 1080 # individually selected cells (ctrl-click) 1081 selected_cells += self.GetSelectedCells() 1082 1083 # selected rows 1084 selected_cells += list ( 1085 (row, col) 1086 for row in sel_rows 1087 for col in xrange(self.GetNumberCols()) 1088 ) 1089 1090 # selected columns 1091 selected_cells += list ( 1092 (row, col) 1093 for row in xrange(self.GetNumberRows()) 1094 for col in sel_cols 1095 ) 1096 1097 # selection blocks 1098 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 1099 selected_cells += [ 1100 (row, col) 1101 for row in xrange(top_left[0], bottom_right[0] + 1) 1102 for col in xrange(top_left[1], bottom_right[1] + 1) 1103 ] 1104 1105 return set(selected_cells)
1106 #------------------------------------------------------------
1107 - def get_selected_rows(self):
1108 rows = {} 1109 1110 for row, col in self.get_selected_cells(): 1111 rows[row] = True 1112 1113 return rows.keys()
1114 #------------------------------------------------------------
1115 - def get_selected_data(self):
1116 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1117 #------------------------------------------------------------
1118 - def repopulate_grid(self):
1119 1120 self.empty_grid() 1121 1122 if self.__patient is None: 1123 return 1124 1125 emr = self.__patient.get_emr() 1126 meds = emr.get_current_substance_intake ( 1127 order_by = self.__grouping2order_by_clauses[self.__grouping_mode], 1128 include_unapproved = self.__filter_show_unapproved, 1129 include_inactive = self.__filter_show_inactive 1130 ) 1131 if not meds: 1132 return 1133 1134 self.BeginBatch() 1135 1136 # columns 1137 labels = self.__grouping2col_labels[self.__grouping_mode] 1138 if self.__filter_show_unapproved: 1139 self.AppendCols(numCols = len(labels) + 1) 1140 else: 1141 self.AppendCols(numCols = len(labels)) 1142 for col_idx in range(len(labels)): 1143 self.SetColLabelValue(col_idx, labels[col_idx]) 1144 if self.__filter_show_unapproved: 1145 self.SetColLabelValue(len(labels), u'OK?') 1146 self.SetColSize(len(labels), 40) 1147 1148 self.AppendRows(numRows = len(meds)) 1149 1150 # loop over data 1151 for row_idx in range(len(meds)): 1152 med = meds[row_idx] 1153 self.__row_data[row_idx] = med 1154 1155 if med['is_currently_active'] is True: 1156 atcs = [] 1157 if med['atc_substance'] is not None: 1158 atcs.append(med['atc_substance']) 1159 if med['atc_brand'] is not None: 1160 atcs.append(med['atc_brand']) 1161 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand']) 1162 if allg not in [None, False]: 1163 attr = self.GetOrCreateCellAttr(row_idx, 0) 1164 if allg['type'] == u'allergy': 1165 attr.SetTextColour('red') 1166 else: 1167 attr.SetTextColour('yellow') 1168 self.SetRowAttr(row_idx, attr) 1169 else: 1170 attr = self.GetOrCreateCellAttr(row_idx, 0) 1171 attr.SetTextColour('grey') 1172 self.SetRowAttr(row_idx, attr) 1173 1174 if self.__grouping_mode == u'episode': 1175 if med['pk_episode'] is None: 1176 self.__prev_cell_0 = None 1177 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1178 else: 1179 if self.__prev_cell_0 != med['episode']: 1180 self.__prev_cell_0 = med['episode'] 1181 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u'')) 1182 1183 self.SetCellValue(row_idx, 1, med['substance']) 1184 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u'')) 1185 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u'')) 1186 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1187 1188 if med['is_long_term']: 1189 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1190 else: 1191 if med['duration'] is None: 1192 self.SetCellValue(row_idx, 5, u'') 1193 else: 1194 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1195 1196 if med['pk_brand'] is None: 1197 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1198 else: 1199 if med['fake_brand']: 1200 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1201 else: 1202 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1203 1204 elif self.__grouping_mode == u'brand': 1205 1206 if med['pk_brand'] is None: 1207 self.__prev_cell_0 = None 1208 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1209 else: 1210 if self.__prev_cell_0 != med['brand']: 1211 self.__prev_cell_0 = med['brand'] 1212 if med['fake_brand']: 1213 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1214 else: 1215 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'')) 1216 1217 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u'')) 1218 self.SetCellValue(row_idx, 2, med['substance']) 1219 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u'')) 1220 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1221 1222 if med['is_long_term']: 1223 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1224 else: 1225 if med['duration'] is None: 1226 self.SetCellValue(row_idx, 5, u'') 1227 else: 1228 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1229 1230 if med['pk_episode'] is None: 1231 self.SetCellValue(row_idx, 6, u'') 1232 else: 1233 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u'')) 1234 1235 else: 1236 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode) 1237 1238 if self.__filter_show_unapproved: 1239 self.SetCellValue ( 1240 row_idx, 1241 len(labels), 1242 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?') 1243 ) 1244 1245 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 1246 1247 self.EndBatch()
1248 #------------------------------------------------------------
1249 - def empty_grid(self):
1250 self.BeginBatch() 1251 self.ClearGrid() 1252 # Windows cannot do "nothing", it rather decides to assert() 1253 # on thinking it is supposed to do nothing 1254 if self.GetNumberRows() > 0: 1255 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 1256 if self.GetNumberCols() > 0: 1257 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 1258 self.EndBatch() 1259 self.__row_data = {} 1260 self.__prev_cell_0 = None
1261 #------------------------------------------------------------
1262 - def show_info_on_entry(self):
1263 1264 if len(self.__row_data) == 0: 1265 return 1266 1267 sel_rows = self.get_selected_rows() 1268 if len(sel_rows) != 1: 1269 return 1270 1271 drug_db = get_drug_database() 1272 if drug_db is None: 1273 return 1274 1275 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1276 #------------------------------------------------------------
1278 1279 if len(self.__row_data) == 0: 1280 return 1281 1282 sel_rows = self.get_selected_rows() 1283 1284 if len(sel_rows) != 1: 1285 return 1286 1287 webbrowser.open ( 1288 url = gmMedication.drug2renal_insufficiency_url(search_term = self.get_selected_data()[0]), 1289 new = False, 1290 autoraise = True 1291 )
1292 #------------------------------------------------------------
1293 - def check_interactions(self):
1294 1295 if len(self.__row_data) == 0: 1296 return 1297 1298 drug_db = get_drug_database() 1299 if drug_db is None: 1300 return 1301 1302 if len(self.get_selected_rows()) > 1: 1303 drug_db.check_drug_interactions(substances = self.get_selected_data()) 1304 else: 1305 drug_db.check_drug_interactions(substances = self.__row_data.values())
1306 #------------------------------------------------------------
1307 - def add_substance(self):
1308 edit_intake_of_substance(parent = self, substance = None)
1309 #------------------------------------------------------------
1310 - def edit_substance(self):
1311 1312 rows = self.get_selected_rows() 1313 1314 if len(rows) == 0: 1315 return 1316 1317 if len(rows) > 1: 1318 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True) 1319 return 1320 1321 subst = self.get_selected_data()[0] 1322 edit_intake_of_substance(parent = self, substance = subst)
1323 #------------------------------------------------------------
1324 - def delete_substance(self):
1325 1326 rows = self.get_selected_rows() 1327 1328 if len(rows) == 0: 1329 return 1330 1331 if len(rows) > 1: 1332 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True) 1333 return 1334 1335 subst = self.get_selected_data()[0] 1336 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1337 #------------------------------------------------------------
1339 rows = self.get_selected_rows() 1340 1341 if len(rows) == 0: 1342 return 1343 1344 if len(rows) > 1: 1345 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True) 1346 return 1347 1348 subst = self.get_selected_data()[0] 1349 if subst['is_currently_active']: 1350 subst['discontinued'] = gmDateTime.pydt_now_here() 1351 if subst['discontinue_reason'] is None: 1352 subst['discontinue_reason'] = _('discontinued due to allergy or intolerance') 1353 subst.save() 1354 1355 emr = self.__patient.get_emr() 1356 allg = subst.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 1357 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 1358 dlg.ShowModal()
1359 #------------------------------------------------------------
1360 - def print_medication_list(self):
1361 # there could be some filtering/user interaction going on here 1362 _cfg = gmCfg2.gmCfgData() 1363 print_medication_list(parent = self, cleanup = _cfg.get(option = 'debug'))
1364 #------------------------------------------------------------
1365 - def get_row_tooltip(self, row=None):
1366 1367 try: 1368 entry = self.__row_data[row] 1369 except KeyError: 1370 return u' ' 1371 1372 emr = self.__patient.get_emr() 1373 atcs = [] 1374 if entry['atc_substance'] is not None: 1375 atcs.append(entry['atc_substance']) 1376 if entry['atc_brand'] is not None: 1377 atcs.append(entry['atc_brand']) 1378 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand']) 1379 1380 tt = _('Substance intake entry (%s, %s) [#%s] \n') % ( 1381 gmTools.bool2subst ( 1382 boolean = entry['is_currently_active'], 1383 true_return = gmTools.bool2subst ( 1384 boolean = entry['seems_inactive'], 1385 true_return = _('active, needs check'), 1386 false_return = _('active'), 1387 none_return = _('assumed active') 1388 ), 1389 false_return = _('inactive') 1390 ), 1391 gmTools.bool2subst ( 1392 boolean = entry['intake_is_approved_of'], 1393 true_return = _('approved'), 1394 false_return = _('unapproved') 1395 ), 1396 entry['pk_substance_intake'] 1397 ) 1398 1399 if allg not in [None, False]: 1400 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected')) 1401 tt += u'\n' 1402 tt += u' !! ---- Cave ---- !!\n' 1403 tt += u' %s (%s): %s (%s)\n' % ( 1404 allg['l10n_type'], 1405 certainty, 1406 allg['descriptor'], 1407 gmTools.coalesce(allg['reaction'], u'')[:40] 1408 ) 1409 tt += u'\n' 1410 1411 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance']) 1412 tt += u' ' + _('Preparation: %s\n') % entry['preparation'] 1413 if entry['strength'] is not None: 1414 tt += u' ' + _('Amount per dose: %s') % entry['strength'] 1415 if entry.ddd is not None: 1416 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit']) 1417 tt += u'\n' 1418 else: 1419 if entry.ddd is not None: 1420 tt += u' DDD: %s %s' % (entry.ddd['ddd'], entry.ddd['unit']) 1421 tt += u'\n' 1422 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n')) 1423 1424 tt += u'\n' 1425 1426 tt += gmTools.coalesce ( 1427 entry['brand'], 1428 u'', 1429 _(' Brand name: %%s [#%s]\n') % entry['pk_brand'] 1430 ) 1431 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n')) 1432 1433 tt += u'\n' 1434 1435 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n')) 1436 1437 if entry['is_long_term']: 1438 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity) 1439 else: 1440 if entry['duration'] is None: 1441 duration = u'' 1442 else: 1443 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days)) 1444 1445 tt += _(' Started %s%s%s\n') % ( 1446 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1447 duration, 1448 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'') 1449 ) 1450 1451 if entry['discontinued'] is not None: 1452 tt += _(' Discontinued %s\n') % ( 1453 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1454 ) 1455 tt += _(' Reason: %s\n') % entry['discontinue_reason'] 1456 1457 tt += u'\n' 1458 1459 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n')) 1460 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n')) 1461 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n')) 1462 1463 tt += u'\n' 1464 1465 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({ 1466 'row_ver': entry['row_version'], 1467 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()), 1468 'mod_by': entry['modified_by'] 1469 }) 1470 1471 return tt
1472 #------------------------------------------------------------ 1473 # internal helpers 1474 #------------------------------------------------------------
1475 - def __init_ui(self):
1476 self.CreateGrid(0, 1) 1477 self.EnableEditing(0) 1478 self.EnableDragGridSize(1) 1479 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows) 1480 1481 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER) 1482 1483 self.SetRowLabelSize(0) 1484 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1485 #------------------------------------------------------------ 1486 # properties 1487 #------------------------------------------------------------
1488 - def _get_patient(self):
1489 return self.__patient
1490
1491 - def _set_patient(self, patient):
1492 self.__patient = patient 1493 self.repopulate_grid()
1494 1495 patient = property(_get_patient, _set_patient) 1496 #------------------------------------------------------------
1497 - def _get_grouping_mode(self):
1498 return self.__grouping_mode
1499
1500 - def _set_grouping_mode(self, mode):
1501 self.__grouping_mode = mode 1502 self.repopulate_grid()
1503 1504 grouping_mode = property(_get_grouping_mode, _set_grouping_mode) 1505 #------------------------------------------------------------
1507 return self.__filter_show_unapproved
1508
1509 - def _set_filter_show_unapproved(self, val):
1510 self.__filter_show_unapproved = val 1511 self.repopulate_grid()
1512 1513 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved) 1514 #------------------------------------------------------------
1515 - def _get_filter_show_inactive(self):
1516 return self.__filter_show_inactive
1517
1518 - def _set_filter_show_inactive(self, val):
1519 self.__filter_show_inactive = val 1520 self.repopulate_grid()
1521 1522 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive) 1523 #------------------------------------------------------------ 1524 # event handling 1525 #------------------------------------------------------------
1526 - def __register_events(self):
1527 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 1528 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 1529 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 1530 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 1531 1532 # editing cells 1533 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1534 #------------------------------------------------------------
1535 - def __on_mouse_over_cells(self, evt):
1536 """Calculate where the mouse is and set the tooltip dynamically.""" 1537 1538 # Use CalcUnscrolledPosition() to get the mouse position within the 1539 # entire grid including what's offscreen 1540 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 1541 1542 # use this logic to prevent tooltips outside the actual cells 1543 # apply to GetRowSize, too 1544 # tot = 0 1545 # for col in xrange(self.NumberCols): 1546 # tot += self.GetColSize(col) 1547 # if xpos <= tot: 1548 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 1549 # self.GetColLabelValue(col)) 1550 # break 1551 # else: # mouse is in label area beyond the right-most column 1552 # self.tool_tip.Tip = '' 1553 1554 row, col = self.XYToCell(x, y) 1555 1556 if row == self.__prev_tooltip_row: 1557 return 1558 1559 self.__prev_tooltip_row = row 1560 1561 try: 1562 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row)) 1563 except KeyError: 1564 pass
1565 #------------------------------------------------------------
1566 - def __on_cell_left_dclicked(self, evt):
1567 row = evt.GetRow() 1568 data = self.__row_data[row] 1569 edit_intake_of_substance(parent = self, substance = data)
1570 #============================================================ 1571 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl 1572
1573 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1574 1575 """Panel holding a grid with current substances. Used as notebook page.""" 1576
1577 - def __init__(self, *args, **kwargs):
1578 1579 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs) 1580 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1581 1582 self.__register_interests()
1583 #----------------------------------------------------- 1584 # reget-on-paint mixin API 1585 #-----------------------------------------------------
1586 - def _populate_with_data(self):
1587 """Populate cells with data from model.""" 1588 pat = gmPerson.gmCurrentPatient() 1589 if pat.connected: 1590 self._grid_substances.patient = pat 1591 else: 1592 self._grid_substances.patient = None 1593 return True
1594 #-------------------------------------------------------- 1595 # event handling 1596 #--------------------------------------------------------
1597 - def __register_interests(self):
1598 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1599 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget) 1600 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1601 # active_substance_mod_db 1602 # substance_brand_mod_db 1603 #--------------------------------------------------------
1604 - def _on_pre_patient_selection(self):
1605 wx.CallAfter(self.__on_pre_patient_selection)
1606 #--------------------------------------------------------
1607 - def __on_pre_patient_selection(self):
1608 self._grid_substances.patient = None
1609 #--------------------------------------------------------
1610 - def _on_add_button_pressed(self, event):
1611 self._grid_substances.add_substance()
1612 #--------------------------------------------------------
1613 - def _on_edit_button_pressed(self, event):
1614 self._grid_substances.edit_substance()
1615 #--------------------------------------------------------
1616 - def _on_delete_button_pressed(self, event):
1617 self._grid_substances.delete_substance()
1618 #--------------------------------------------------------
1619 - def _on_info_button_pressed(self, event):
1620 self._grid_substances.show_info_on_entry()
1621 #--------------------------------------------------------
1622 - def _on_interactions_button_pressed(self, event):
1623 self._grid_substances.check_interactions()
1624 #--------------------------------------------------------
1625 - def _on_episode_grouping_selected(self, event):
1626 self._grid_substances.grouping_mode = 'episode'
1627 #--------------------------------------------------------
1628 - def _on_brand_grouping_selected(self, event):
1629 self._grid_substances.grouping_mode = 'brand'
1630 #--------------------------------------------------------
1631 - def _on_show_unapproved_checked(self, event):
1632 self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
1633 #--------------------------------------------------------
1634 - def _on_show_inactive_checked(self, event):
1635 self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
1636 #--------------------------------------------------------
1637 - def _on_print_button_pressed(self, event):
1638 self._grid_substances.print_medication_list()
1639 #--------------------------------------------------------
1640 - def _on_allergy_button_pressed(self, event):
1641 self._grid_substances.create_allergy_from_substance()
1642 #--------------------------------------------------------
1643 - def _on_button_kidneys_pressed(self, event):
1644 self._grid_substances.show_renal_insufficiency_info()
1645 #============================================================ 1646 # main 1647 #------------------------------------------------------------ 1648 if __name__ == '__main__': 1649 1650 if len(sys.argv) < 2: 1651 sys.exit() 1652 1653 if sys.argv[1] != 'test': 1654 sys.exit() 1655 1656 from Gnumed.pycommon import gmI18N 1657 1658 gmI18N.activate_locale() 1659 gmI18N.install_domain(domain = 'gnumed') 1660 1661 #---------------------------------------- 1662 # test_*() 1663 pass 1664 1665 #============================================================ 1666