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