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

Source Code for Module Gnumed.wxpython.gmMedicationWidgets

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