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 ( 519 description || ' (' || preparation || ')' || 'coalesce(' [' || atc_code || ']', '') 520 ) AS brand 521 FROM ref.branded_drug 522 WHERE description %(fragment_condition)s 523 ORDER BY brand 524 LIMIT 50""" 525 526 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 527 mp.setThresholds(2, 3, 4) 528 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 529 self.SetToolTipString(_('The brand name of the drug the patient is taking.')) 530 self.matcher = mp 531 self.selection_only = False
532 533 #============================================================ 534 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl 535
536 -class cCurrentMedicationEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
537
538 - def __init__(self, *args, **kwargs):
539 540 try: 541 data = kwargs['substance'] 542 del kwargs['substance'] 543 except KeyError: 544 data = None 545 546 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs) 547 gmEditArea.cGenericEditAreaMixin.__init__(self) 548 self.mode = 'new' 549 self.data = data 550 if data is not None: 551 self.mode = 'edit' 552 553 self.__init_ui()
554 #----------------------------------------------------------------
555 - def __init_ui(self):
556 557 # adjust phrasewheels 558 559 self._PRW_brand.add_callback_on_lose_focus(callback = self._on_leave_brand)
560 #----------------------------------------------------------------
561 - def __refresh_allergies(self):
562 emr = gmPerson.gmCurrentPatient().get_emr() 563 564 state = emr.allergy_state 565 if state['last_confirmed'] is None: 566 confirmed = _('never') 567 else: 568 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding()) 569 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed) 570 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by']) 571 msg += u'\n' 572 573 for allergy in emr.get_allergies(): 574 msg += u'%s (%s, %s): %s\n' % ( 575 allergy['descriptor'], 576 allergy['l10n_type'], 577 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'), 578 gmTools.coalesce(allergy['reaction'], _('reaction not recorded')) 579 ) 580 581 self._LBL_allergies.SetLabel(msg)
582 #----------------------------------------------------------------
584 585 if self._PRW_brand.GetData() is None: 586 self._TCTRL_brand_ingredients.SetValue(u'') 587 if self.data is None: 588 return 589 if self.data['pk_brand'] is None: 590 return 591 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand']) 592 593 brand = gmMedication.cBrandedDrug(aPK_obj = self._PRW_brand.GetData()) 594 595 if self.data is None: 596 self._PRW_preparation.SetText(brand['preparation'], None) 597 else: 598 self._PRW_preparation.SetText ( 599 gmTools.coalesce(self.data['preparation'], brand['preparation']), 600 self.data['preparation'] 601 ) 602 603 comps = brand.components 604 605 if comps is None: 606 return 607 608 if len(comps) == 0: 609 return 610 611 comps = u' / '.join([ u'%s%s' % (c['description'], gmTools.coalesce(c['atc_code'], u'', u' (%s)')) for c in comps ]) 612 self._TCTRL_brand_ingredients.SetValue(comps)
613 #---------------------------------------------------------------- 614 # generic Edit Area mixin API 615 #----------------------------------------------------------------
616 - def _valid_for_save(self):
617 618 validity = True 619 620 if self._PRW_substance.GetValue().strip() == u'': 621 self._PRW_substance.display_as_valid(False) 622 validity = False 623 else: 624 self._PRW_substance.display_as_valid(True) 625 626 if self._PRW_preparation.GetValue().strip() == u'': 627 self._PRW_preparation.display_as_valid(False) 628 validity = False 629 else: 630 self._PRW_preparation.display_as_valid(True) 631 632 if self._CHBOX_approved.IsChecked(): 633 if self._PRW_episode.GetValue().strip() == u'': 634 self._PRW_episode.display_as_valid(False) 635 validity = False 636 else: 637 self._PRW_episode.display_as_valid(True) 638 639 if self._CHBOX_approved.IsChecked() is True: 640 self._PRW_duration.display_as_valid(True) 641 else: 642 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 643 self._PRW_duration.display_as_valid(True) 644 else: 645 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None: 646 self._PRW_duration.display_as_valid(False) 647 validity = False 648 else: 649 self._PRW_duration.display_as_valid(True) 650 651 end = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True) 652 if end is not None: 653 start = self._DP_started.GetValue(as_pydt = True) 654 if start > end: 655 self._DP_started.display_as_valid(False) 656 self._DP_discontinued.display_as_valid(False) 657 validity = False 658 else: 659 self._DP_started.display_as_valid(True) 660 self._DP_discontinued.display_as_valid(True) 661 662 if validity is False: 663 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save substance intake. Invalid or missing essential input.')) 664 665 return validity
666 #----------------------------------------------------------------
667 - def _save_as_new(self):
668 669 emr = gmPerson.gmCurrentPatient().get_emr() 670 671 # 1) create substance intake entry 672 if self._PRW_substance.GetData() is None: 673 subst = self._PRW_substance.GetValue().strip() 674 else: 675 # normalize, do not simply re-use name from phrasewheel 676 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData())['description'] 677 678 intake = emr.add_substance_intake ( 679 substance = subst, 680 episode = self._PRW_episode.GetData(can_create = True), 681 preparation = self._PRW_preparation.GetValue() 682 ) 683 684 intake['strength'] = self._PRW_strength.GetValue() 685 intake['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True) 686 intake['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True) 687 if intake['discontinued'] is None: 688 intake['discontinue_reason'] = None 689 else: 690 intake['discontinue_reason'] = self._PRW_discontinue_reason().GetValue().strip() 691 intake['schedule'] = self._PRW_schedule.GetValue() 692 intake['aim'] = self._PRW_aim.GetValue() 693 intake['notes'] = self._PRW_notes.GetValue() 694 intake['is_long_term'] = self._CHBOX_long_term.IsChecked() 695 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 696 697 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 698 intake['duration'] = None 699 else: 700 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 701 702 # 2) create or retrieve brand 703 brand = None 704 pk_brand = self._PRW_brand.GetData() 705 706 # brand pre-selected ? 707 if pk_brand is None: 708 # no, so ... 709 desc = self._PRW_brand.GetValue().strip() 710 if desc != u'': 711 # ... create or get it 712 brand = gmMedication.create_branded_drug ( 713 brand_name = desc, 714 preparation = self._PRW_preparation.GetValue().strip(), 715 return_existing = True 716 ) 717 pk_brand = brand['pk'] 718 else: 719 # yes, so get it 720 brand = gmMedication.cBrandedDrug(aPK_obj = pk_brand) 721 722 # 3) link brand, if available 723 intake['pk_brand'] = pk_brand 724 intake.save() 725 726 # brand neither creatable nor pre-selected 727 if brand is None: 728 self.data = intake 729 return True 730 731 # 4) add substance to brand as component (because 732 # that's effectively what we are saying here) 733 # FIXME: we may want to ask the user here 734 # FIXME: or only do it if there are no components yet 735 if self._PRW_substance.GetData() is None: 736 brand.add_component(substance = self._PRW_substance.GetValue().strip()) 737 else: 738 # normalize substance name 739 subst = gmMedication.get_substance_by_pk(pk = self._PRW_substance.GetData()) 740 if subst is not None: 741 brand.add_component(substance = subst['description']) 742 743 self.data = intake 744 745 if self._CHBOX_is_allergy.IsChecked(): 746 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 747 # open for editing 748 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 749 dlg.ShowModal() 750 751 return True
752 #----------------------------------------------------------------
753 - def _save_as_update(self):
754 755 if self._PRW_substance.GetData() is None: 756 self.data['pk_substance'] = gmMedication.create_used_substance ( 757 substance = self._PRW_substance.GetValue().strip() 758 )['pk'] 759 else: 760 self.data['pk_substance'] = self._PRW_substance.GetData() 761 762 self.data['started'] = self._DP_started.GetValue(as_pydt = True, invalid_as_none = True) 763 self.data['discontinued'] = self._DP_discontinued.GetValue(as_pydt = True, invalid_as_none = True) 764 if self.data['discontinued'] is None: 765 self.data['discontinue_reason'] = None 766 else: 767 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip() 768 self.data['preparation'] = self._PRW_preparation.GetValue() 769 self.data['strength'] = self._PRW_strength.GetValue() 770 self.data['schedule'] = self._PRW_schedule.GetValue() 771 self.data['aim'] = self._PRW_aim.GetValue() 772 self.data['notes'] = self._PRW_notes.GetValue() 773 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked() 774 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 775 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True) 776 777 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 778 self.data['duration'] = None 779 else: 780 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 781 782 if self._PRW_brand.GetData() is None: 783 desc = self._PRW_brand.GetValue().strip() 784 if desc != u'': 785 # create or get brand 786 self.data['pk_brand'] = gmMedication.create_branded_drug ( 787 brand_name = desc, 788 preparation = self._PRW_preparation.GetValue().strip(), 789 return_existing = True 790 )['pk'] 791 else: 792 self.data['pk_brand'] = self._PRW_brand.GetData() 793 794 self.data.save() 795 796 if self._CHBOX_is_allergy.IsChecked(): 797 allg = self.data.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 798 # open for editing 799 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 800 dlg.ShowModal() 801 802 return True
803 #----------------------------------------------------------------
804 - def _refresh_as_new(self):
805 self._PRW_substance.SetText(u'', None) 806 self._PRW_strength.SetText(u'', None) 807 # self._PRW_preparation.SetText(u'', None) 808 self._PRW_schedule.SetText(u'', None) 809 self._PRW_duration.SetText(u'', None) 810 self._PRW_aim.SetText(u'', None) 811 self._PRW_notes.SetText(u'', None) 812 self._PRW_episode.SetText(u'', None) 813 814 self._CHBOX_long_term.SetValue(False) 815 self._CHBOX_approved.SetValue(True) 816 817 self._DP_started.SetValue(gmDateTime.pydt_now_here()) 818 self._DP_discontinued.SetValue(None) 819 self._PRW_discontinue_reason.SetValue(u'') 820 821 self.__refresh_brand_and_components() 822 self.__refresh_allergies() 823 824 self._PRW_substance.SetFocus()
825 #----------------------------------------------------------------
826 - def _refresh_from_existing(self):
827 828 self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance']) 829 self._PRW_strength.SetText(gmTools.coalesce(self.data['strength'], u''), self.data['strength']) 830 831 if self.data['is_long_term']: 832 self._CHBOX_long_term.SetValue(True) 833 self._PRW_duration.Enable(False) 834 self._PRW_duration.SetText(gmTools.u_infinity, None) 835 self._BTN_discontinued_as_planned.Enable(False) 836 else: 837 self._CHBOX_long_term.SetValue(False) 838 self._PRW_duration.Enable(True) 839 self._BTN_discontinued_as_planned.Enable(True) 840 if self.data['duration'] is None: 841 self._PRW_duration.SetText(u'', None) 842 else: 843 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration']) 844 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim']) 845 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes']) 846 self._PRW_episode.SetData(self.data['pk_episode']) 847 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule']) 848 849 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of']) 850 851 self._DP_started.SetValue(self.data['started']) 852 self._DP_discontinued.SetValue(self.data['discontinued']) 853 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u'')) 854 855 self.__refresh_brand_and_components() 856 self.__refresh_allergies() 857 858 self._PRW_substance.SetFocus()
859 #----------------------------------------------------------------
861 self._refresh_as_new()
862 #---------------------------------------------------------------- 863 # event handlers 864 #----------------------------------------------------------------
865 - def _on_leave_brand(self):
866 self.__refresh_brand_and_components()
867 #----------------------------------------------------------------
868 - def _on_discontinued_date_changed(self, event):
869 if self._DP_discontinued.GetValue() is None: 870 self._PRW_discontinue_reason.Enable(False) 871 self._CHBOX_is_allergy.Enable(False) 872 else: 873 self._PRW_discontinue_reason.Enable(True) 874 self._CHBOX_is_allergy.Enable(True)
875 #----------------------------------------------------------------
876 - def _on_get_substance_button_pressed(self, event):
877 drug_db = get_drug_database() 878 if drug_db is None: 879 return 880 881 result = drug_db.import_drugs() 882 if result is None: 883 return 884 885 new_drugs, new_substances = result 886 if len(new_substances) == 0: 887 return 888 889 # FIXME: could usefully 890 # FIXME: a) ask which to post-process 891 # FIXME: b) remember the others for post-processing 892 first = new_substances[0] 893 self._PRW_substance.SetText(first['description'], first['pk'])
894 #----------------------------------------------------------------
895 - def _on_get_brand_button_pressed(self, event):
896 drug_db = get_drug_database() 897 self.__refresh_allergies() 898 if drug_db is None: 899 return 900 901 result = drug_db.import_drugs() 902 self.__refresh_allergies() 903 if result is None: 904 return 905 906 new_drugs, new_substances = result 907 if len(new_drugs) == 0: 908 return 909 # FIXME: could usefully 910 # FIXME: a) ask which to post-process 911 # FIXME: b) remember the others for post-processing 912 first = new_drugs[0] 913 self._PRW_brand.SetText(first['description'], first['pk']) 914 915 self.__refresh_brand_and_components()
916 #----------------------------------------------------------------
918 919 now = gmDateTime.pydt_now_here() 920 921 self.__refresh_allergies() 922 923 # do we have a (full) plan ? 924 if None not in [self.data['started'], self.data['duration']]: 925 planned_end = self.data['started'] + self.data['duration'] 926 # the plan hasn't ended so [Per plan] can't apply ;-) 927 if planned_end > now: 928 return 929 self._DP_discontinued.SetValue(planned_end) 930 self._PRW_discontinue_reason.Enable(True) 931 self._PRW_discontinue_reason.SetValue(u'') 932 self._CHBOX_is_allergy.Enable(True) 933 return 934 935 # we know started but not duration: apparently the plan is to stop today 936 if self.data['started'] is not None: 937 # but we haven't started yet so we can't stop 938 if self.data['started'] > now: 939 return 940 941 self._DP_discontinued.SetValue(now) 942 self._PRW_discontinue_reason.Enable(True) 943 self._PRW_discontinue_reason.SetValue(u'') 944 self._CHBOX_is_allergy.Enable(True)
945 #----------------------------------------------------------------
946 - def _on_chbox_long_term_checked(self, event):
947 if self._CHBOX_long_term.IsChecked() is True: 948 self._PRW_duration.Enable(False) 949 self._BTN_discontinued_as_planned.Enable(False) 950 self._PRW_discontinue_reason.Enable(False) 951 self._CHBOX_is_allergy.Enable(False) 952 else: 953 self._PRW_duration.Enable(True) 954 self._BTN_discontinued_as_planned.Enable(True) 955 self._PRW_discontinue_reason.Enable(True) 956 self._CHBOX_is_allergy.Enable(True) 957 958 self.__refresh_allergies()
959 #----------------------------------------------------------------
960 - def _on_chbox_is_allergy_checked(self, event):
961 if self._CHBOX_is_allergy.IsChecked() is True: 962 val = self._PRW_discontinue_reason.GetValue().strip() 963 if not val.startswith(_('not tolerated:')): 964 self._PRW_discontinue_reason.SetValue(u'%s %s' % (_('not tolerated:'), val)) 965 966 self.__refresh_allergies()
967 #============================================================
968 -def delete_substance_intake(parent=None, substance=None):
969 delete_it = gmGuiHelpers.gm_show_question ( 970 aMessage = _( 971 'Do you really want to remove this substance intake ?\n' 972 '\n' 973 'It may be prudent to edit the details first so as to\n' 974 'leave behind some indication of why it was deleted.\n' 975 ), 976 aTitle = _('Deleting medication / substance intake') 977 ) 978 if not delete_it: 979 return 980 981 gmMedication.delete_substance_intake(substance = substance)
982 #------------------------------------------------------------
983 -def edit_intake_of_substance(parent = None, substance=None):
984 ea = cCurrentMedicationEAPnl(parent = parent, id = -1, substance = substance) 985 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None)) 986 dlg.SetTitle(gmTools.coalesce(substance, _('Adding substance intake'), _('Editing substance intake'))) 987 if dlg.ShowModal() == wx.ID_OK: 988 dlg.Destroy() 989 return True 990 dlg.Destroy() 991 return False
992 #============================================================ 993 # current substances grid 994 #------------------------------------------------------------
995 -def configure_medication_list_template(parent=None):
996 997 if parent is None: 998 parent = wx.GetApp().GetTopWindow() 999 1000 template = gmFormWidgets.manage_form_templates ( 1001 parent = parent, 1002 template_types = ['current medication list'] 1003 ) 1004 option = u'form_templates.medication_list' 1005 1006 if template is None: 1007 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 1008 return None 1009 1010 if template['engine'] != u'L': 1011 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 1012 return None 1013 1014 dbcfg = gmCfg.cCfgSQL() 1015 dbcfg.set ( 1016 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1017 option = option, 1018 value = u'%s - %s' % (template['name_long'], template['external_version']) 1019 ) 1020 1021 return template
1022 #------------------------------------------------------------ 1104 #------------------------------------------------------------
1105 -class cCurrentSubstancesGrid(wx.grid.Grid):
1106 """A grid class for displaying current substance intake. 1107 1108 - does NOT listen to the currently active patient 1109 - thereby it can display any patient at any time 1110 """
1111 - def __init__(self, *args, **kwargs):
1112 1113 wx.grid.Grid.__init__(self, *args, **kwargs) 1114 1115 self.__patient = None 1116 self.__row_data = {} 1117 self.__prev_row = None 1118 self.__prev_tooltip_row = None 1119 self.__prev_cell_0 = None 1120 self.__grouping_mode = u'episode' 1121 self.__filter_show_unapproved = False 1122 self.__filter_show_inactive = False 1123 1124 self.__grouping2col_labels = { 1125 u'episode': [ 1126 _('Episode'), 1127 _('Substance'), 1128 _('Dose'), 1129 _('Schedule'), 1130 _('Started'), 1131 _('Duration'), 1132 _('Brand') 1133 ], 1134 u'brand': [ 1135 _('Brand'), 1136 _('Schedule'), 1137 _('Substance'), 1138 _('Dose'), 1139 _('Started'), 1140 _('Duration'), 1141 _('Episode') 1142 ] 1143 } 1144 1145 self.__grouping2order_by_clauses = { 1146 u'episode': u'pk_health_issue nulls first, episode, substance, started', 1147 u'brand': u'brand nulls last, substance, started' 1148 } 1149 1150 self.__init_ui() 1151 self.__register_events()
1152 #------------------------------------------------------------ 1153 # external API 1154 #------------------------------------------------------------
1155 - def get_selected_cells(self):
1156 1157 sel_block_top_left = self.GetSelectionBlockTopLeft() 1158 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 1159 sel_cols = self.GetSelectedCols() 1160 sel_rows = self.GetSelectedRows() 1161 1162 selected_cells = [] 1163 1164 # individually selected cells (ctrl-click) 1165 selected_cells += self.GetSelectedCells() 1166 1167 # selected rows 1168 selected_cells += list ( 1169 (row, col) 1170 for row in sel_rows 1171 for col in xrange(self.GetNumberCols()) 1172 ) 1173 1174 # selected columns 1175 selected_cells += list ( 1176 (row, col) 1177 for row in xrange(self.GetNumberRows()) 1178 for col in sel_cols 1179 ) 1180 1181 # selection blocks 1182 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 1183 selected_cells += [ 1184 (row, col) 1185 for row in xrange(top_left[0], bottom_right[0] + 1) 1186 for col in xrange(top_left[1], bottom_right[1] + 1) 1187 ] 1188 1189 return set(selected_cells)
1190 #------------------------------------------------------------
1191 - def get_selected_rows(self):
1192 rows = {} 1193 1194 for row, col in self.get_selected_cells(): 1195 rows[row] = True 1196 1197 return rows.keys()
1198 #------------------------------------------------------------
1199 - def get_selected_data(self):
1200 return [ self.__row_data[row] for row in self.get_selected_rows() ]
1201 #------------------------------------------------------------
1202 - def repopulate_grid(self):
1203 1204 self.empty_grid() 1205 1206 if self.__patient is None: 1207 return 1208 1209 emr = self.__patient.get_emr() 1210 meds = emr.get_current_substance_intake ( 1211 order_by = self.__grouping2order_by_clauses[self.__grouping_mode], 1212 include_unapproved = self.__filter_show_unapproved, 1213 include_inactive = self.__filter_show_inactive 1214 ) 1215 if not meds: 1216 return 1217 1218 self.BeginBatch() 1219 1220 # columns 1221 labels = self.__grouping2col_labels[self.__grouping_mode] 1222 if self.__filter_show_unapproved: 1223 self.AppendCols(numCols = len(labels) + 1) 1224 else: 1225 self.AppendCols(numCols = len(labels)) 1226 for col_idx in range(len(labels)): 1227 self.SetColLabelValue(col_idx, labels[col_idx]) 1228 if self.__filter_show_unapproved: 1229 self.SetColLabelValue(len(labels), u'OK?') 1230 self.SetColSize(len(labels), 40) 1231 1232 self.AppendRows(numRows = len(meds)) 1233 1234 # loop over data 1235 for row_idx in range(len(meds)): 1236 med = meds[row_idx] 1237 self.__row_data[row_idx] = med 1238 1239 if med['is_currently_active'] is True: 1240 atcs = [] 1241 if med['atc_substance'] is not None: 1242 atcs.append(med['atc_substance']) 1243 # if med['atc_brand'] is not None: 1244 # atcs.append(med['atc_brand']) 1245 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand']) 1246 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],)) 1247 if allg not in [None, False]: 1248 attr = self.GetOrCreateCellAttr(row_idx, 0) 1249 if allg['type'] == u'allergy': 1250 attr.SetTextColour('red') 1251 else: 1252 attr.SetTextColour('yellow') 1253 self.SetRowAttr(row_idx, attr) 1254 else: 1255 attr = self.GetOrCreateCellAttr(row_idx, 0) 1256 attr.SetTextColour('grey') 1257 self.SetRowAttr(row_idx, attr) 1258 1259 if self.__grouping_mode == u'episode': 1260 if med['pk_episode'] is None: 1261 self.__prev_cell_0 = None 1262 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1263 else: 1264 if self.__prev_cell_0 != med['episode']: 1265 self.__prev_cell_0 = med['episode'] 1266 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['episode'], u'')) 1267 1268 self.SetCellValue(row_idx, 1, med['substance']) 1269 self.SetCellValue(row_idx, 2, gmTools.coalesce(med['strength'], u'')) 1270 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u'')) 1271 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1272 1273 if med['is_long_term']: 1274 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1275 else: 1276 if med['duration'] is None: 1277 self.SetCellValue(row_idx, 5, u'') 1278 else: 1279 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1280 1281 if med['pk_brand'] is None: 1282 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1283 else: 1284 if med['fake_brand']: 1285 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1286 else: 1287 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['brand'], u'')) 1288 1289 elif self.__grouping_mode == u'brand': 1290 1291 if med['pk_brand'] is None: 1292 self.__prev_cell_0 = None 1293 self.SetCellValue(row_idx, 0, gmTools.u_diameter) 1294 else: 1295 if self.__prev_cell_0 != med['brand']: 1296 self.__prev_cell_0 = med['brand'] 1297 if med['fake_brand']: 1298 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'', _('%s (fake)'))) 1299 else: 1300 self.SetCellValue(row_idx, 0, gmTools.coalesce(med['brand'], u'')) 1301 1302 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u'')) 1303 self.SetCellValue(row_idx, 2, med['substance']) 1304 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['strength'], u'')) 1305 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 1306 1307 if med['is_long_term']: 1308 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 1309 else: 1310 if med['duration'] is None: 1311 self.SetCellValue(row_idx, 5, u'') 1312 else: 1313 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 1314 1315 if med['pk_episode'] is None: 1316 self.SetCellValue(row_idx, 6, u'') 1317 else: 1318 self.SetCellValue(row_idx, 6, gmTools.coalesce(med['episode'], u'')) 1319 1320 else: 1321 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode) 1322 1323 if self.__filter_show_unapproved: 1324 self.SetCellValue ( 1325 row_idx, 1326 len(labels), 1327 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?') 1328 ) 1329 1330 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 1331 1332 self.EndBatch()
1333 #------------------------------------------------------------
1334 - def empty_grid(self):
1335 self.BeginBatch() 1336 self.ClearGrid() 1337 # Windows cannot do "nothing", it rather decides to assert() 1338 # on thinking it is supposed to do nothing 1339 if self.GetNumberRows() > 0: 1340 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 1341 if self.GetNumberCols() > 0: 1342 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 1343 self.EndBatch() 1344 self.__row_data = {} 1345 self.__prev_cell_0 = None
1346 #------------------------------------------------------------
1347 - def show_info_on_entry(self):
1348 1349 if len(self.__row_data) == 0: 1350 return 1351 1352 sel_rows = self.get_selected_rows() 1353 if len(sel_rows) != 1: 1354 return 1355 1356 drug_db = get_drug_database() 1357 if drug_db is None: 1358 return 1359 1360 drug_db.show_info_on_substance(substance = self.get_selected_data()[0])
1361 #------------------------------------------------------------
1363 1364 if len(self.__row_data) == 0: 1365 return 1366 1367 sel_rows = self.get_selected_rows() 1368 1369 if len(sel_rows) != 1: 1370 return 1371 1372 webbrowser.open ( 1373 url = gmMedication.drug2renal_insufficiency_url(search_term = self.get_selected_data()[0]), 1374 new = False, 1375 autoraise = True 1376 )
1377 #------------------------------------------------------------
1378 - def report_ADR(self):
1379 1380 dbcfg = gmCfg.cCfgSQL() 1381 1382 url = dbcfg.get2 ( 1383 option = u'external.urls.report_ADR', 1384 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1385 bias = u'user', 1386 default = u'https://dcgma.org/uaw/meldung.php' 1387 ) 1388 1389 webbrowser.open(url = url, new = False, autoraise = True)
1390 #------------------------------------------------------------
1391 - def check_interactions(self):
1392 1393 if len(self.__row_data) == 0: 1394 return 1395 1396 drug_db = get_drug_database() 1397 if drug_db is None: 1398 return 1399 1400 if len(self.get_selected_rows()) > 1: 1401 drug_db.check_drug_interactions(substances = self.get_selected_data()) 1402 else: 1403 drug_db.check_drug_interactions(substances = self.__row_data.values())
1404 #------------------------------------------------------------
1405 - def add_substance(self):
1406 edit_intake_of_substance(parent = self, substance = None)
1407 #------------------------------------------------------------
1408 - def edit_substance(self):
1409 1410 rows = self.get_selected_rows() 1411 1412 if len(rows) == 0: 1413 return 1414 1415 if len(rows) > 1: 1416 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True) 1417 return 1418 1419 subst = self.get_selected_data()[0] 1420 edit_intake_of_substance(parent = self, substance = subst)
1421 #------------------------------------------------------------
1422 - def delete_substance(self):
1423 1424 rows = self.get_selected_rows() 1425 1426 if len(rows) == 0: 1427 return 1428 1429 if len(rows) > 1: 1430 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True) 1431 return 1432 1433 subst = self.get_selected_data()[0] 1434 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
1435 #------------------------------------------------------------
1437 rows = self.get_selected_rows() 1438 1439 if len(rows) == 0: 1440 return 1441 1442 if len(rows) > 1: 1443 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True) 1444 return 1445 1446 subst = self.get_selected_data()[0] 1447 if subst['is_currently_active']: 1448 subst['discontinued'] = gmDateTime.pydt_now_here() 1449 if subst['discontinue_reason'] is None: 1450 subst['discontinue_reason'] = _('discontinued due to allergy or intolerance') 1451 subst.save() 1452 1453 emr = self.__patient.get_emr() 1454 allg = subst.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 1455 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = self, id = -1) 1456 dlg.ShowModal()
1457 #------------------------------------------------------------
1458 - def print_medication_list(self):
1459 # there could be some filtering/user interaction going on here 1460 _cfg = gmCfg2.gmCfgData() 1461 print_medication_list(parent = self, cleanup = _cfg.get(option = 'debug'))
1462 #------------------------------------------------------------
1463 - def get_row_tooltip(self, row=None):
1464 1465 try: 1466 entry = self.__row_data[row] 1467 except KeyError: 1468 return u' ' 1469 1470 emr = self.__patient.get_emr() 1471 atcs = [] 1472 if entry['atc_substance'] is not None: 1473 atcs.append(entry['atc_substance']) 1474 # if entry['atc_brand'] is not None: 1475 # atcs.append(entry['atc_brand']) 1476 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand']) 1477 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],)) 1478 1479 tt = _('Substance intake entry (%s, %s) [#%s] \n') % ( 1480 gmTools.bool2subst ( 1481 boolean = entry['is_currently_active'], 1482 true_return = gmTools.bool2subst ( 1483 boolean = entry['seems_inactive'], 1484 true_return = _('active, needs check'), 1485 false_return = _('active'), 1486 none_return = _('assumed active') 1487 ), 1488 false_return = _('inactive') 1489 ), 1490 gmTools.bool2subst ( 1491 boolean = entry['intake_is_approved_of'], 1492 true_return = _('approved'), 1493 false_return = _('unapproved') 1494 ), 1495 entry['pk_substance_intake'] 1496 ) 1497 1498 if allg not in [None, False]: 1499 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected')) 1500 tt += u'\n' 1501 tt += u' !! ---- Cave ---- !!\n' 1502 tt += u' %s (%s): %s (%s)\n' % ( 1503 allg['l10n_type'], 1504 certainty, 1505 allg['descriptor'], 1506 gmTools.coalesce(allg['reaction'], u'')[:40] 1507 ) 1508 tt += u'\n' 1509 1510 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance']) 1511 tt += u' ' + _('Preparation: %s\n') % entry['preparation'] 1512 if entry['strength'] is not None: 1513 tt += u' ' + _('Amount per dose: %s') % entry['strength'] 1514 if entry.ddd is not None: 1515 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit']) 1516 tt += u'\n' 1517 else: 1518 if entry.ddd is not None: 1519 tt += u' DDD: %s %s' % (entry.ddd['ddd'], entry.ddd['unit']) 1520 tt += u'\n' 1521 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n')) 1522 1523 tt += u'\n' 1524 1525 tt += gmTools.coalesce ( 1526 entry['brand'], 1527 u'', 1528 _(' Brand name: %%s [#%s]\n') % entry['pk_brand'] 1529 ) 1530 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n')) 1531 1532 tt += u'\n' 1533 1534 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n')) 1535 1536 if entry['is_long_term']: 1537 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity) 1538 else: 1539 if entry['duration'] is None: 1540 duration = u'' 1541 else: 1542 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days)) 1543 1544 tt += _(' Started %s%s%s\n') % ( 1545 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1546 duration, 1547 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'') 1548 ) 1549 1550 if entry['discontinued'] is not None: 1551 tt += _(' Discontinued %s\n') % ( 1552 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 1553 ) 1554 tt += _(' Reason: %s\n') % entry['discontinue_reason'] 1555 1556 tt += u'\n' 1557 1558 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n')) 1559 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n')) 1560 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n')) 1561 1562 tt += u'\n' 1563 1564 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({ 1565 'row_ver': entry['row_version'], 1566 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()), 1567 'mod_by': entry['modified_by'] 1568 }) 1569 1570 return tt
1571 #------------------------------------------------------------ 1572 # internal helpers 1573 #------------------------------------------------------------
1574 - def __init_ui(self):
1575 self.CreateGrid(0, 1) 1576 self.EnableEditing(0) 1577 self.EnableDragGridSize(1) 1578 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows) 1579 1580 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER) 1581 1582 self.SetRowLabelSize(0) 1583 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
1584 #------------------------------------------------------------ 1585 # properties 1586 #------------------------------------------------------------
1587 - def _get_patient(self):
1588 return self.__patient
1589
1590 - def _set_patient(self, patient):
1591 self.__patient = patient 1592 self.repopulate_grid()
1593 1594 patient = property(_get_patient, _set_patient) 1595 #------------------------------------------------------------
1596 - def _get_grouping_mode(self):
1597 return self.__grouping_mode
1598
1599 - def _set_grouping_mode(self, mode):
1600 self.__grouping_mode = mode 1601 self.repopulate_grid()
1602 1603 grouping_mode = property(_get_grouping_mode, _set_grouping_mode) 1604 #------------------------------------------------------------
1606 return self.__filter_show_unapproved
1607
1608 - def _set_filter_show_unapproved(self, val):
1609 self.__filter_show_unapproved = val 1610 self.repopulate_grid()
1611 1612 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved) 1613 #------------------------------------------------------------
1614 - def _get_filter_show_inactive(self):
1615 return self.__filter_show_inactive
1616
1617 - def _set_filter_show_inactive(self, val):
1618 self.__filter_show_inactive = val 1619 self.repopulate_grid()
1620 1621 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive) 1622 #------------------------------------------------------------ 1623 # event handling 1624 #------------------------------------------------------------
1625 - def __register_events(self):
1626 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 1627 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 1628 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 1629 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 1630 1631 # editing cells 1632 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1633 #------------------------------------------------------------
1634 - def __on_mouse_over_cells(self, evt):
1635 """Calculate where the mouse is and set the tooltip dynamically.""" 1636 1637 # Use CalcUnscrolledPosition() to get the mouse position within the 1638 # entire grid including what's offscreen 1639 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 1640 1641 # use this logic to prevent tooltips outside the actual cells 1642 # apply to GetRowSize, too 1643 # tot = 0 1644 # for col in xrange(self.NumberCols): 1645 # tot += self.GetColSize(col) 1646 # if xpos <= tot: 1647 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 1648 # self.GetColLabelValue(col)) 1649 # break 1650 # else: # mouse is in label area beyond the right-most column 1651 # self.tool_tip.Tip = '' 1652 1653 row, col = self.XYToCell(x, y) 1654 1655 if row == self.__prev_tooltip_row: 1656 return 1657 1658 self.__prev_tooltip_row = row 1659 1660 try: 1661 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row)) 1662 except KeyError: 1663 pass
1664 #------------------------------------------------------------
1665 - def __on_cell_left_dclicked(self, evt):
1666 row = evt.GetRow() 1667 data = self.__row_data[row] 1668 edit_intake_of_substance(parent = self, substance = data)
1669 #============================================================ 1670 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl 1671
1672 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
1673 1674 """Panel holding a grid with current substances. Used as notebook page.""" 1675
1676 - def __init__(self, *args, **kwargs):
1677 1678 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs) 1679 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1680 1681 self.__register_interests()
1682 #----------------------------------------------------- 1683 # reget-on-paint mixin API 1684 #-----------------------------------------------------
1685 - def _populate_with_data(self):
1686 """Populate cells with data from model.""" 1687 pat = gmPerson.gmCurrentPatient() 1688 if pat.connected: 1689 self._grid_substances.patient = pat 1690 else: 1691 self._grid_substances.patient = None 1692 return True
1693 #-------------------------------------------------------- 1694 # event handling 1695 #--------------------------------------------------------
1696 - def __register_interests(self):
1697 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1698 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget) 1699 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
1700 # active_substance_mod_db 1701 # substance_brand_mod_db 1702 #--------------------------------------------------------
1703 - def _on_pre_patient_selection(self):
1704 wx.CallAfter(self.__on_pre_patient_selection)
1705 #--------------------------------------------------------
1706 - def __on_pre_patient_selection(self):
1707 self._grid_substances.patient = None
1708 #--------------------------------------------------------
1709 - def _on_add_button_pressed(self, event):
1710 self._grid_substances.add_substance()
1711 #--------------------------------------------------------
1712 - def _on_edit_button_pressed(self, event):
1713 self._grid_substances.edit_substance()
1714 #--------------------------------------------------------
1715 - def _on_delete_button_pressed(self, event):
1716 self._grid_substances.delete_substance()
1717 #--------------------------------------------------------
1718 - def _on_info_button_pressed(self, event):
1719 self._grid_substances.show_info_on_entry()
1720 #--------------------------------------------------------
1721 - def _on_interactions_button_pressed(self, event):
1722 self._grid_substances.check_interactions()
1723 #--------------------------------------------------------
1724 - def _on_episode_grouping_selected(self, event):
1725 self._grid_substances.grouping_mode = 'episode'
1726 #--------------------------------------------------------
1727 - def _on_brand_grouping_selected(self, event):
1728 self._grid_substances.grouping_mode = 'brand'
1729 #--------------------------------------------------------
1730 - def _on_show_unapproved_checked(self, event):
1731 self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
1732 #--------------------------------------------------------
1733 - def _on_show_inactive_checked(self, event):
1734 self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
1735 #--------------------------------------------------------
1736 - def _on_print_button_pressed(self, event):
1737 self._grid_substances.print_medication_list()
1738 #--------------------------------------------------------
1739 - def _on_allergy_button_pressed(self, event):
1740 self._grid_substances.create_allergy_from_substance()
1741 #--------------------------------------------------------
1742 - def _on_button_kidneys_pressed(self, event):
1743 self._grid_substances.show_renal_insufficiency_info()
1744 #--------------------------------------------------------
1745 - def _on_adr_button_pressed(self, event):
1746 self._grid_substances.report_ADR()
1747 #============================================================ 1748 # main 1749 #------------------------------------------------------------ 1750 if __name__ == '__main__': 1751 1752 if len(sys.argv) < 2: 1753 sys.exit() 1754 1755 if sys.argv[1] != 'test': 1756 sys.exit() 1757 1758 from Gnumed.pycommon import gmI18N 1759 1760 gmI18N.activate_locale() 1761 gmI18N.install_domain(domain = 'gnumed') 1762 1763 #---------------------------------------- 1764 app = wx.PyWidgetTester(size = (600, 600)) 1765 app.SetWidget(cATCPhraseWheel, -1) 1766 app.MainLoop() 1767 1768 #============================================================ 1769