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.SetText(u'', 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 # event handlers 774 #----------------------------------------------------------------
775 - def _on_leave_brand(self):
776 self.__refresh_brand_and_components()
777 #----------------------------------------------------------------
778 - def _on_discontinued_date_changed(self, event):
779 if self._DP_discontinued.GetValue() is None: 780 self._PRW_discontinue_reason.Enable(False) 781 self._CHBOX_is_allergy.Enable(False) 782 else: 783 self._PRW_discontinue_reason.Enable(True) 784 self._CHBOX_is_allergy.Enable(True)
785 #----------------------------------------------------------------
786 - def _on_get_substance_button_pressed(self, event):
787 drug_db = get_drug_database() 788 if drug_db is None: 789 return 790 791 result = drug_db.import_drugs() 792 if result is None: 793 return 794 795 new_drugs, new_substances = result 796 if len(new_substances) == 0: 797 return 798 799 # FIXME: could usefully 800 # FIXME: a) ask which to post-process 801 # FIXME: b) remember the others for post-processing 802 first = new_substances[0] 803 self._PRW_substance.SetText(first['description'], first['pk'])
804 #----------------------------------------------------------------
805 - def _on_get_brand_button_pressed(self, event):
806 drug_db = get_drug_database() 807 self.__refresh_allergies() 808 if drug_db is None: 809 return 810 811 result = drug_db.import_drugs() 812 self.__refresh_allergies() 813 if result is None: 814 return 815 816 new_drugs, new_substances = result 817 if len(new_drugs) == 0: 818 return 819 # FIXME: could usefully 820 # FIXME: a) ask which to post-process 821 # FIXME: b) remember the others for post-processing 822 first = new_drugs[0] 823 self._PRW_brand.SetText(first['description'], first['pk']) 824 825 self.__refresh_brand_and_components()
826 #----------------------------------------------------------------
828 829 now = gmDateTime.pydt_now_here() 830 831 self.__refresh_allergies() 832 833 # do we have a (full) plan ? 834 if None not in [self.data['started'], self.data['duration']]: 835 planned_end = self.data['started'] + self.data['duration'] 836 # the plan hasn't ended so [Per plan] can't apply ;-) 837 if planned_end > now: 838 return 839 self._DP_discontinued.SetValue(planned_end) 840 self._PRW_discontinue_reason.Enable(True) 841 self._PRW_discontinue_reason.SetValue(u'') 842 self._CHBOX_is_allergy.Enable(True) 843 return 844 845 # we know started but not duration: apparently the plan is to stop today 846 if self.data['started'] is not None: 847 # but we haven't started yet so we can't stop 848 if self.data['started'] > now: 849 return 850 851 self._DP_discontinued.SetValue(now) 852 self._PRW_discontinue_reason.Enable(True) 853 self._PRW_discontinue_reason.SetValue(u'') 854 self._CHBOX_is_allergy.Enable(True)
855 #----------------------------------------------------------------
856 - def _on_chbox_long_term_checked(self, event):
857 if self._CHBOX_long_term.IsChecked() is True: 858 self._PRW_duration.Enable(False) 859 self._BTN_discontinued_as_planned.Enable(False) 860 self._PRW_discontinue_reason.Enable(False) 861 self._CHBOX_is_allergy.Enable(False) 862 else: 863 self._PRW_duration.Enable(True) 864 self._BTN_discontinued_as_planned.Enable(True) 865 self._PRW_discontinue_reason.Enable(True) 866 self._CHBOX_is_allergy.Enable(True) 867 868 self.__refresh_allergies()
869 #----------------------------------------------------------------
870 - def _on_chbox_is_allergy_checked(self, event):
871 if self._CHBOX_is_allergy.IsChecked() is True: 872 val = self._PRW_discontinue_reason.GetValue().strip() 873 if not val.startswith(_('not tolerated:')): 874 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val)) 875 876 self.__refresh_allergies()
877 #============================================================
878 -def delete_substance_intake(parent=None, substance=None):
879 delete_it = gmGuiHelpers.gm_show_question ( 880 aMessage = _( 881 'Do you really want to remove this substance intake ?\n' 882 '\n' 883 'It may be prudent to edit the details first so as to\n' 884 'leave behind some indication of why it was deleted.\n' 885 ), 886 aTitle = _('Deleting medication / substance intake') 887 ) 888 if not delete_it: 889 return 890 891 gmMedication.delete_substance_intake(substance = substance)
892 #------------------------------------------------------------
893 -def edit_intake_of_substance(parent = None, substance=None):
894 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance) 895 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None)) 896 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake'))) 897 if dlg.ShowModal() == wx.ID_OK: 898 dlg.Destroy() 899 return True 900 dlg.Destroy() 901 return False
902 #============================================================ 903 # current substances grid 904 #------------------------------------------------------------
905 -def configure_medication_list_template(parent=None):
906 907 if parent is None: 908 parent = wx.GetApp().GetTopWindow() 909 910 template = gmFormWidgets.manage_form_templates ( 911 parent = parent, 912 template_types = ['current medication list'] 913 ) 914 option = u'form_templates.medication_list' 915 916 if template is None: 917 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 918 return None 919 920 if template['engine'] != u'L': 921 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 922 return None 923 924 dbcfg = gmCfg.cCfgSQL() 925 dbcfg.set ( 926 workplace = gmSurgery.gmCurrentPractice().active_workplace, 927 option = option, 928 value = u'%s - %s' % (template['name_long'], template['external_version']) 929 ) 930 931 return template
932 #------------------------------------------------------------ 1014 #------------------------------------------------------------
1015 -class cCurrentSubstancesGrid(wx.grid.Grid):
1016 """A grid class for displaying current substance intake. 1017 1018 - does NOT listen to the currently active patient 1019 - thereby it can display any patient at any time 1020 """
1021 - def __init__(self, *args, **kwargs):
1022 1023 wx.grid.Grid.__init__(self, *args, **kwargs) 1024 1025 self.__patient = None 1026 self.__row_data = {} 1027 self.__prev_row = None 1028 self.__prev_tooltip_row = None 1029 self.__prev_cell_0 = None 1030 self.__grouping_mode = u'episode' 1031 self.__filter_show_unapproved = False 1032 self.__filter_show_inactive = False 1033 1034 self.__grouping2col_labels = { 1035 u'episode': [ 1036 _('Episode'), 1037 _('Substance'), 1038 _('Dose'), 1039 _('Schedule'), 1040 _('Started'), 1041 _('Duration'), 1042 _('Brand') 1043 ], 1044 u'brand': [ 1045 _('Brand'), 1046 _('Schedule'), 1047 _('Substance'), 1048 _('Dose'), 1049 _('Started'), 1050 _('Duration'), 1051 _('Episode') 1052 ] 1053 } 1054 1055 self.__grouping2order_by_clauses = { 1056 u'episode': u'pk_health_issue nulls first, episode, substance, started', 1057 u'brand': u'brand nulls last, substance, started' 1058 } 1059 1060 self.__init_ui() 1061 self.__register_events()
1062 #------------------------------------------------------------ 1063 # external API 1064 #------------------------------------------------------------
1065 - def get_selected_cells(self):
1066 1067 sel_block_top_left = self.GetSelectionBlockTopLeft() 1068 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 1069 sel_cols = self.GetSelectedCols() 1070 sel_rows = self.GetSelectedRows() 1071 1072 selected_cells = [] 1073 1074 # individually selected cells (ctrl-click) 1075 selected_cells += self.GetSelectedCells() 1076 1077 # selected rows 1078 selected_cells += list ( 1079 (row, col) 1080 for row in sel_rows 1081 for col in xrange(self.GetNumberCols()) 1082 ) 1083 1084 # selected columns 1085 selected_cells += list ( 1086 (row, col) 1087 for row in xrange(self.GetNumberRows()) 1088 for col in sel_cols 1089 ) 1090 1091 # selection blocks 1092 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 1093 selected_cells += [ 1094 (row, col) 1095 for row in xrange(top_left[0], bottom_right[0] + 1) 1096 for col in xrange(top_left[1], bottom_right[1] + 1) 1097 ] 1098 1099 return set(selected_cells)
1100 #------------------------------------------------------------
1101 - def get_selected_rows(self):
1102 rows = {} 1103 1104 for row, col in self.get_selected_cells(): 1105 rows[row] = True 1106 1107 return rows.keys()
1108 #------------------------------------------------------------
1109 - def get_selected_data(self):
1110 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1111 #------------------------------------------------------------
1112 - def repopulate_grid(self):
1113 1114 self.empty_grid() 1115 1116 if self.__patient is None: 1117 return 1118 1119 emr = self.__patient.get_emr() 1120 meds = emr.get_current_substance_intake ( 1121 order_by = self.__grouping2order_by_clauses[self.__grouping_mode], 1122 include_unapproved = self.__filter_show_unapproved, 1123 include_inactive = self.__filter_show_inactive 1124 ) 1125 if not meds: 1126 return 1127 1128 self.BeginBatch() 1129 1130 # columns 1131 labels = self.__grouping2col_labels[self.__grouping_mode] 1132 if self.__filter_show_unapproved: 1133 self.AppendCols(numCols = len(labels) + 1) 1134 else: 1135 self.AppendCols(numCols = len(labels)) 1136 for col_idx in range(len(labels)): 1137 self.SetColLabelValue(col_idx, labels[col_idx]) 1138 if self.__filter_show_unapproved: 1139 self.SetColLabelValue(len(labels), u'OK?') 1140 self.SetColSize(len(labels), 40) 1141 1142 self.AppendRows(numRows = len(meds)) 1143 1144 # loop over data 1145 for row_idx in range(len(meds)): 1146 med = meds[row_idx] 1147 self.__row_data[row_idx] = med 1148 1149 if med['is_currently_active'] is True: 1150 atcs = [] 1151 if med['atc_substance'] is not None: 1152 atcs.append(med['atc_substance']) 1153 if med['atc_brand'] is not None: 1154 atcs.append(med['atc_brand']) 1155 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand']) 1156 if allg not in [None, False]: 1157 attr = self.GetOrCreateCellAttr(row_idx, 0) 1158 if allg['type'] == u'allergy': 1159 attr.SetTextColour('red') 1160 else: 1161 attr.SetTextColour('yellow') 1162 self.SetRowAttr(row_idx, attr) 1163 else: 1164 attr = self.GetOrCreateCellAttr(row_idx, 0) 1165 attr.SetTextColour('grey') 1166 self.SetRowAttr(row_idx, attr) 1167 1168 if self.__grouping_mode == u'episode': 1169 if med['pk_episode'] is None: 1170 self.__prev_cell_0 = None 1171 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1172 else: 1173 if self.__prev_cell_0 != med['episode']: 1174 self.__prev_cell_0 = med['episode'] 1175 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u'')) 1176 1177 self.SetCellValue(row_idx, 1, med['substance']) 1178 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u'')) 1179 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u'')) 1180 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1181 1182 if med['is_long_term']: 1183 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1184 else: 1185 if med['duration'] is None: 1186 self.SetCellValue(row_idx, 5, u'') 1187 else: 1188 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1189 1190 if med['pk_brand'] is None: 1191 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1192 else: 1193 if med['fake_brand']: 1194 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1195 else: 1196 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1197 1198 elif self.__grouping_mode == u'brand': 1199 1200 if med['pk_brand'] is None: 1201 self.__prev_cell_0 = None 1202 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1203 else: 1204 if self.__prev_cell_0 != med['brand']: 1205 self.__prev_cell_0 = med['brand'] 1206 if med['fake_brand']: 1207 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1208 else: 1209 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'')) 1210 1211 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u'')) 1212 self.SetCellValue(row_idx, 2, med['substance']) 1213 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u'')) 1214 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1215 1216 if med['is_long_term']: 1217 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1218 else: 1219 if med['duration'] is None: 1220 self.SetCellValue(row_idx, 5, u'') 1221 else: 1222 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1223 1224 if med['pk_episode'] is None: 1225 self.SetCellValue(row_idx, 6, u'') 1226 else: 1227 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u'')) 1228 1229 else: 1230 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode) 1231 1232 if self.__filter_show_unapproved: 1233 self.SetCellValue ( 1234 row_idx, 1235 len(labels), 1236 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?') 1237 ) 1238 1239 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 1240 1241 self.EndBatch()
1242 #------------------------------------------------------------
1243 - def empty_grid(self):
1244 self.BeginBatch() 1245 self.ClearGrid() 1246 # Windows cannot do "nothing", it rather decides to assert() 1247 # on thinking it is supposed to do nothing 1248 if self.GetNumberRows() > 0: 1249 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 1250 if self.GetNumberCols() > 0: 1251 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 1252 self.EndBatch() 1253 self.__row_data = {} 1254 self.__prev_cell_0 = None
1255 #------------------------------------------------------------
1256 - def show_info_on_entry(self):
1257 1258 if len(self.__row_data) == 0: 1259 return 1260 1261 sel_rows = self.get_selected_rows() 1262 if len(sel_rows) != 1: 1263 return 1264 1265 drug_db = get_drug_database() 1266 if drug_db is None: 1267 return 1268 1269 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1270 #------------------------------------------------------------
1272 1273 if len(self.__row_data) == 0: 1274 return 1275 1276 sel_rows = self.get_selected_rows() 1277 1278 if len(sel_rows) != 1: 1279 return 1280 1281 webbrowser.open ( 1282 url = gmMedication.drug2renal_insufficiency_url(search_term = self.get_selected_data()[0]), 1283 new = False, 1284 autoraise = True 1285 )
1286 #------------------------------------------------------------
1287 - def report_ADR(self):
1288 1289 dbcfg = gmCfg.cCfgSQL() 1290 1291 url = dbcfg.get2 ( 1292 option = u'external.urls.report_ADR', 1293 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1294 bias = u'user', 1295 default = u'https://dcgma.org/uaw/meldung.php' 1296 ) 1297 1298 webbrowser.open(url = url, new = False, autoraise = True)
1299 #------------------------------------------------------------
1300 - def check_interactions(self):
1301 1302 if len(self.__row_data) == 0: 1303 return 1304 1305 drug_db = get_drug_database() 1306 if drug_db is None: 1307 return 1308 1309 if len(self.get_selected_rows()) > 1: 1310 drug_db.check_drug_interactions(substances = self.get_selected_data()) 1311 else: 1312 drug_db.check_drug_interactions(substances = self.__row_data.values())
1313 #------------------------------------------------------------
1314 - def add_substance(self):
1315 edit_intake_of_substance(parent = self, substance = None)
1316 #------------------------------------------------------------
1317 - def edit_substance(self):
1318 1319 rows = self.get_selected_rows() 1320 1321 if len(rows) == 0: 1322 return 1323 1324 if len(rows) > 1: 1325 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True) 1326 return 1327 1328 subst = self.get_selected_data()[0] 1329 edit_intake_of_substance(parent = self, substance = subst)
1330 #------------------------------------------------------------
1331 - def delete_substance(self):
1332 1333 rows = self.get_selected_rows() 1334 1335 if len(rows) == 0: 1336 return 1337 1338 if len(rows) > 1: 1339 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True) 1340 return 1341 1342 subst = self.get_selected_data()[0] 1343 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1344 #------------------------------------------------------------
1346 rows = self.get_selected_rows() 1347 1348 if len(rows) == 0: 1349 return 1350 1351 if len(rows) > 1: 1352 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True) 1353 return 1354 1355 subst = self.get_selected_data()[0] 1356 if subst['is_currently_active']: 1357 subst['discontinued'] = gmDateTime.pydt_now_here() 1358 if subst['discontinue_reason'] is None: 1359 subst['discontinue_reason'] = _('discontinued due to allergy or intolerance') 1360 subst.save() 1361 1362 emr = self.__patient.get_emr() 1363 allg = subst.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 1364 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 1365 dlg.ShowModal()
1366 #------------------------------------------------------------
1367 - def print_medication_list(self):
1368 # there could be some filtering/user interaction going on here 1369 _cfg = gmCfg2.gmCfgData() 1370 print_medication_list(parent = self, cleanup = _cfg.get(option = 'debug'))
1371 #------------------------------------------------------------
1372 - def get_row_tooltip(self, row=None):
1373 1374 try: 1375 entry = self.__row_data[row] 1376 except KeyError: 1377 return u' ' 1378 1379 emr = self.__patient.get_emr() 1380 atcs = [] 1381 if entry['atc_substance'] is not None: 1382 atcs.append(entry['atc_substance']) 1383 if entry['atc_brand'] is not None: 1384 atcs.append(entry['atc_brand']) 1385 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand']) 1386 1387 tt = _('Substance intake entry (%s, %s) [#%s] \n') % ( 1388 gmTools.bool2subst ( 1389 boolean = entry['is_currently_active'], 1390 true_return = gmTools.bool2subst ( 1391 boolean = entry['seems_inactive'], 1392 true_return = _('active, needs check'), 1393 false_return = _('active'), 1394 none_return = _('assumed active') 1395 ), 1396 false_return = _('inactive') 1397 ), 1398 gmTools.bool2subst ( 1399 boolean = entry['intake_is_approved_of'], 1400 true_return = _('approved'), 1401 false_return = _('unapproved') 1402 ), 1403 entry['pk_substance_intake'] 1404 ) 1405 1406 if allg not in [None, False]: 1407 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected')) 1408 tt += u'\n' 1409 tt += u' !! ---- Cave ---- !!\n' 1410 tt += u' %s (%s): %s (%s)\n' % ( 1411 allg['l10n_type'], 1412 certainty, 1413 allg['descriptor'], 1414 gmTools.coalesce(allg['reaction'], u'')[:40] 1415 ) 1416 tt += u'\n' 1417 1418 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance']) 1419 tt += u' ' + _('Preparation: %s\n') % entry['preparation'] 1420 if entry['strength'] is not None: 1421 tt += u' ' + _('Amount per dose: %s') % entry['strength'] 1422 if entry.ddd is not None: 1423 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit']) 1424 tt += u'\n' 1425 else: 1426 if entry.ddd is not None: 1427 tt += u' DDD: %s %s' % (entry.ddd['ddd'], entry.ddd['unit']) 1428 tt += u'\n' 1429 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n')) 1430 1431 tt += u'\n' 1432 1433 tt += gmTools.coalesce ( 1434 entry['brand'], 1435 u'', 1436 _(' Brand name: %%s [#%s]\n') % entry['pk_brand'] 1437 ) 1438 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n')) 1439 1440 tt += u'\n' 1441 1442 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n')) 1443 1444 if entry['is_long_term']: 1445 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity) 1446 else: 1447 if entry['duration'] is None: 1448 duration = u'' 1449 else: 1450 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days)) 1451 1452 tt += _(' Started %s%s%s\n') % ( 1453 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1454 duration, 1455 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'') 1456 ) 1457 1458 if entry['discontinued'] is not None: 1459 tt += _(' Discontinued %s\n') % ( 1460 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1461 ) 1462 tt += _(' Reason: %s\n') % entry['discontinue_reason'] 1463 1464 tt += u'\n' 1465 1466 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n')) 1467 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n')) 1468 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n')) 1469 1470 tt += u'\n' 1471 1472 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({ 1473 'row_ver': entry['row_version'], 1474 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()), 1475 'mod_by': entry['modified_by'] 1476 }) 1477 1478 return tt
1479 #------------------------------------------------------------ 1480 # internal helpers 1481 #------------------------------------------------------------
1482 - def __init_ui(self):
1483 self.CreateGrid(0, 1) 1484 self.EnableEditing(0) 1485 self.EnableDragGridSize(1) 1486 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows) 1487 1488 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER) 1489 1490 self.SetRowLabelSize(0) 1491 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1492 #------------------------------------------------------------ 1493 # properties 1494 #------------------------------------------------------------
1495 - def _get_patient(self):
1496 return self.__patient
1497
1498 - def _set_patient(self, patient):
1499 self.__patient = patient 1500 self.repopulate_grid()
1501 1502 patient = property(_get_patient, _set_patient) 1503 #------------------------------------------------------------
1504 - def _get_grouping_mode(self):
1505 return self.__grouping_mode
1506
1507 - def _set_grouping_mode(self, mode):
1508 self.__grouping_mode = mode 1509 self.repopulate_grid()
1510 1511 grouping_mode = property(_get_grouping_mode, _set_grouping_mode) 1512 #------------------------------------------------------------
1514 return self.__filter_show_unapproved
1515
1516 - def _set_filter_show_unapproved(self, val):
1517 self.__filter_show_unapproved = val 1518 self.repopulate_grid()
1519 1520 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved) 1521 #------------------------------------------------------------
1522 - def _get_filter_show_inactive(self):
1523 return self.__filter_show_inactive
1524
1525 - def _set_filter_show_inactive(self, val):
1526 self.__filter_show_inactive = val 1527 self.repopulate_grid()
1528 1529 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive) 1530 #------------------------------------------------------------ 1531 # event handling 1532 #------------------------------------------------------------
1533 - def __register_events(self):
1534 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 1535 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 1536 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 1537 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 1538 1539 # editing cells 1540 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1541 #------------------------------------------------------------
1542 - def __on_mouse_over_cells(self, evt):
1543 """Calculate where the mouse is and set the tooltip dynamically.""" 1544 1545 # Use CalcUnscrolledPosition() to get the mouse position within the 1546 # entire grid including what's offscreen 1547 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 1548 1549 # use this logic to prevent tooltips outside the actual cells 1550 # apply to GetRowSize, too 1551 # tot = 0 1552 # for col in xrange(self.NumberCols): 1553 # tot += self.GetColSize(col) 1554 # if xpos <= tot: 1555 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 1556 # self.GetColLabelValue(col)) 1557 # break 1558 # else: # mouse is in label area beyond the right-most column 1559 # self.tool_tip.Tip = '' 1560 1561 row, col = self.XYToCell(x, y) 1562 1563 if row == self.__prev_tooltip_row: 1564 return 1565 1566 self.__prev_tooltip_row = row 1567 1568 try: 1569 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row)) 1570 except KeyError: 1571 pass
1572 #------------------------------------------------------------
1573 - def __on_cell_left_dclicked(self, evt):
1574 row = evt.GetRow() 1575 data = self.__row_data[row] 1576 edit_intake_of_substance(parent = self, substance = data)
1577 #============================================================ 1578 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl 1579
1580 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1581 1582 """Panel holding a grid with current substances. Used as notebook page.""" 1583
1584 - def __init__(self, *args, **kwargs):
1585 1586 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs) 1587 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1588 1589 self.__register_interests()
1590 #----------------------------------------------------- 1591 # reget-on-paint mixin API 1592 #-----------------------------------------------------
1593 - def _populate_with_data(self):
1594 """Populate cells with data from model.""" 1595 pat = gmPerson.gmCurrentPatient() 1596 if pat.connected: 1597 self._grid_substances.patient = pat 1598 else: 1599 self._grid_substances.patient = None 1600 return True
1601 #-------------------------------------------------------- 1602 # event handling 1603 #--------------------------------------------------------
1604 - def __register_interests(self):
1605 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1606 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget) 1607 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1608 # active_substance_mod_db 1609 # substance_brand_mod_db 1610 #--------------------------------------------------------
1611 - def _on_pre_patient_selection(self):
1612 wx.CallAfter(self.__on_pre_patient_selection)
1613 #--------------------------------------------------------
1614 - def __on_pre_patient_selection(self):
1615 self._grid_substances.patient = None
1616 #--------------------------------------------------------
1617 - def _on_add_button_pressed(self, event):
1618 self._grid_substances.add_substance()
1619 #--------------------------------------------------------
1620 - def _on_edit_button_pressed(self, event):
1621 self._grid_substances.edit_substance()
1622 #--------------------------------------------------------
1623 - def _on_delete_button_pressed(self, event):
1624 self._grid_substances.delete_substance()
1625 #--------------------------------------------------------
1626 - def _on_info_button_pressed(self, event):
1627 self._grid_substances.show_info_on_entry()
1628 #--------------------------------------------------------
1629 - def _on_interactions_button_pressed(self, event):
1630 self._grid_substances.check_interactions()
1631 #--------------------------------------------------------
1632 - def _on_episode_grouping_selected(self, event):
1633 self._grid_substances.grouping_mode = 'episode'
1634 #--------------------------------------------------------
1635 - def _on_brand_grouping_selected(self, event):
1636 self._grid_substances.grouping_mode = 'brand'
1637 #--------------------------------------------------------
1638 - def _on_show_unapproved_checked(self, event):
1639 self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
1640 #--------------------------------------------------------
1641 - def _on_show_inactive_checked(self, event):
1642 self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
1643 #--------------------------------------------------------
1644 - def _on_print_button_pressed(self, event):
1645 self._grid_substances.print_medication_list()
1646 #--------------------------------------------------------
1647 - def _on_allergy_button_pressed(self, event):
1648 self._grid_substances.create_allergy_from_substance()
1649 #--------------------------------------------------------
1650 - def _on_button_kidneys_pressed(self, event):
1651 self._grid_substances.show_renal_insufficiency_info()
1652 #--------------------------------------------------------
1653 - def _on_adr_button_pressed(self, event):
1654 self._grid_substances.report_ADR()
1655 #============================================================ 1656 # main 1657 #------------------------------------------------------------ 1658 if __name__ == '__main__': 1659 1660 if len(sys.argv) < 2: 1661 sys.exit() 1662 1663 if sys.argv[1] != 'test': 1664 sys.exit() 1665 1666 from Gnumed.pycommon import gmI18N 1667 1668 gmI18N.activate_locale() 1669 gmI18N.install_domain(domain = 'gnumed') 1670 1671 #---------------------------------------- 1672 # test_*() 1673 pass 1674 1675 #============================================================ 1676