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  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
   5  __license__ = "GPL v2 or later" 
   6   
   7  import logging 
   8  import sys 
   9  import os.path 
  10  import decimal 
  11   
  12   
  13  import wx 
  14  import wx.grid 
  15   
  16   
  17  if __name__ == '__main__': 
  18          sys.path.insert(0, '../../') 
  19  from Gnumed.pycommon import gmDispatcher 
  20  from Gnumed.pycommon import gmCfg 
  21  from Gnumed.pycommon import gmTools 
  22  from Gnumed.pycommon import gmTools 
  23  from Gnumed.pycommon import gmDateTime 
  24  from Gnumed.pycommon import gmMatchProvider 
  25  from Gnumed.pycommon import gmI18N 
  26  from Gnumed.pycommon import gmPrinting 
  27  from Gnumed.pycommon import gmCfg2 
  28  from Gnumed.pycommon import gmNetworkTools 
  29   
  30  from Gnumed.business import gmPerson 
  31  from Gnumed.business import gmATC 
  32  from Gnumed.business import gmSurgery 
  33  from Gnumed.business import gmMedication 
  34  from Gnumed.business import gmForms 
  35  from Gnumed.business import gmStaff 
  36   
  37  from Gnumed.wxpython import gmGuiHelpers 
  38  from Gnumed.wxpython import gmRegetMixin 
  39  from Gnumed.wxpython import gmAuthWidgets 
  40  from Gnumed.wxpython import gmEditArea 
  41  from Gnumed.wxpython import gmMacro 
  42  from Gnumed.wxpython import gmCfgWidgets 
  43  from Gnumed.wxpython import gmListWidgets 
  44  from Gnumed.wxpython import gmPhraseWheel 
  45  from Gnumed.wxpython import gmFormWidgets 
  46  from Gnumed.wxpython import gmAllergyWidgets 
  47   
  48   
  49  _log = logging.getLogger('gm.ui') 
  50   
  51  #============================================================ 
  52  # generic drug database access 
  53  #============================================================ 
54 -def configure_drug_data_source(parent=None):
55 gmCfgWidgets.configure_string_from_list_option ( 56 parent = parent, 57 message = _( 58 '\n' 59 'Please select the default drug data source from the list below.\n' 60 '\n' 61 'Note that to actually use it you need to have the database installed, too.' 62 ), 63 option = 'external.drug_data.default_source', 64 bias = 'user', 65 default_value = None, 66 choices = gmMedication.drug_data_source_interfaces.keys(), 67 columns = [_('Drug data source')], 68 data = gmMedication.drug_data_source_interfaces.keys(), 69 caption = _('Configuring default drug data source') 70 )
71 #============================================================
72 -def get_drug_database(parent = None):
73 dbcfg = gmCfg.cCfgSQL() 74 75 # load from option 76 default_db = dbcfg.get2 ( 77 option = 'external.drug_data.default_source', 78 workplace = gmSurgery.gmCurrentPractice().active_workplace, 79 bias = 'workplace' 80 ) 81 82 # not configured -> try to configure 83 if default_db is None: 84 gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True) 85 configure_drug_data_source(parent = parent) 86 default_db = dbcfg.get2 ( 87 option = 'external.drug_data.default_source', 88 workplace = gmSurgery.gmCurrentPractice().active_workplace, 89 bias = 'workplace' 90 ) 91 # still not configured -> return 92 if default_db is None: 93 gmGuiHelpers.gm_show_error ( 94 aMessage = _('There is no default drug database configured.'), 95 aTitle = _('Jumping to drug database') 96 ) 97 return None 98 99 # now it MUST be configured (either newly or previously) 100 # but also *validly* ? 101 try: 102 drug_db = gmMedication.drug_data_source_interfaces[default_db]() 103 except KeyError: 104 # not valid 105 _log.error('faulty default drug data source configuration: %s', default_db) 106 # try to configure 107 configure_drug_data_source(parent = parent) 108 default_db = dbcfg.get2 ( 109 option = 'external.drug_data.default_source', 110 workplace = gmSurgery.gmCurrentPractice().active_workplace, 111 bias = 'workplace' 112 ) 113 # deconfigured or aborted (and thusly still misconfigured) ? 114 try: 115 drug_db = gmMedication.drug_data_source_interfaces[default_db]() 116 except KeyError: 117 _log.error('still faulty default drug data source configuration: %s', default_db) 118 return None 119 120 pat = gmPerson.gmCurrentPatient() 121 if pat.connected: 122 drug_db.patient = pat 123 124 return drug_db
125 #============================================================
126 -def jump_to_drug_database():
127 dbcfg = gmCfg.cCfgSQL() 128 drug_db = get_drug_database() 129 if drug_db is None: 130 return 131 drug_db.switch_to_frontend(blocking = False)
132 133 #============================================================
134 -def jump_to_ifap(import_drugs=False):
135 136 dbcfg = gmCfg.cCfgSQL() 137 138 ifap_cmd = dbcfg.get2 ( 139 option = 'external.ifap-win.shell_command', 140 workplace = gmSurgery.gmCurrentPractice().active_workplace, 141 bias = 'workplace', 142 default = 'wine "C:\Ifapwin\WIAMDB.EXE"' 143 ) 144 found, binary = gmShellAPI.detect_external_binary(ifap_cmd) 145 if not found: 146 gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd) 147 return False 148 ifap_cmd = binary 149 150 if import_drugs: 151 transfer_file = os.path.expanduser(dbcfg.get2 ( 152 option = 'external.ifap-win.transfer_file', 153 workplace = gmSurgery.gmCurrentPractice().active_workplace, 154 bias = 'workplace', 155 default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv' 156 )) 157 # file must exist for Ifap to write into it 158 try: 159 f = open(transfer_file, 'w+b').close() 160 except IOError: 161 _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file) 162 gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file) 163 return False 164 165 wx.BeginBusyCursor() 166 gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs) 167 wx.EndBusyCursor() 168 169 if import_drugs: 170 # COMMENT: this file must exist PRIOR to invoking IFAP 171 # COMMENT: or else IFAP will not write data into it ... 172 try: 173 csv_file = open(transfer_file, 'rb') # FIXME: encoding 174 except: 175 _log.exception('cannot access [%s]', fname) 176 csv_file = None 177 178 if csv_file is not None: 179 import csv 180 csv_lines = csv.DictReader ( 181 csv_file, 182 fieldnames = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(), 183 delimiter = ';' 184 ) 185 pat = gmPerson.gmCurrentPatient() 186 emr = pat.get_emr() 187 # dummy episode for now 188 epi = emr.add_episode(episode_name = _('Current medication')) 189 for line in csv_lines: 190 narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % ( 191 line['Packungszahl'].strip(), 192 line['Handelsname'].strip(), 193 line['Form'].strip(), 194 line[u'Packungsgr\xf6\xdfe'].strip(), 195 line['Abpackungsmenge'].strip(), 196 line['Einheit'].strip(), 197 line['Hersteller'].strip(), 198 line['PZN'].strip() 199 ) 200 emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi) 201 csv_file.close() 202 203 return True
204 205 #============================================================ 206 # ATC related widgets 207 #============================================================ 208
209 -def browse_atc_reference(parent=None):
210 211 if parent is None: 212 parent = wx.GetApp().GetTopWindow() 213 #------------------------------------------------------------ 214 def refresh(lctrl): 215 atcs = gmATC.get_reference_atcs() 216 217 items = [ [ 218 a['atc'], 219 a['term'], 220 u'%s' % gmTools.coalesce(a['ddd'], u''), 221 gmTools.coalesce(a['unit'], u''), 222 gmTools.coalesce(a['administrative_route'], u''), 223 gmTools.coalesce(a['comment'], u''), 224 a['version'], 225 a['lang'] 226 ] for a in atcs ] 227 lctrl.set_string_items(items) 228 lctrl.set_data(atcs)
229 #------------------------------------------------------------ 230 gmListWidgets.get_choices_from_list ( 231 parent = parent, 232 msg = _('\nThe ATC codes as known to GNUmed.\n'), 233 caption = _('Showing ATC codes.'), 234 columns = [ u'ATC', _('Term'), u'DDD', _('Unit'), _(u'Route'), _('Comment'), _('Version'), _('Language') ], 235 single_selection = True, 236 refresh_callback = refresh 237 ) 238 239 #============================================================
240 -def update_atc_reference_data():
241 242 dlg = wx.FileDialog ( 243 parent = None, 244 message = _('Choose an ATC import config file'), 245 defaultDir = os.path.expanduser(os.path.join('~', 'gnumed')), 246 defaultFile = '', 247 wildcard = "%s (*.conf)|*.conf|%s (*)|*" % (_('config files'), _('all files')), 248 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST 249 ) 250 251 result = dlg.ShowModal() 252 if result == wx.ID_CANCEL: 253 return 254 255 cfg_file = dlg.GetPath() 256 dlg.Destroy() 257 258 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing ATC reference data')) 259 if conn is None: 260 return False 261 262 wx.BeginBusyCursor() 263 264 if gmATC.atc_import(cfg_fname = cfg_file, conn = conn): 265 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported ATC reference data.')) 266 else: 267 gmDispatcher.send(signal = 'statustext', msg = _('Importing ATC reference data failed.'), beep = True) 268 269 wx.EndBusyCursor() 270 return True
271 272 #============================================================ 273
274 -class cATCPhraseWheel(gmPhraseWheel.cPhraseWheel):
275
276 - def __init__(self, *args, **kwargs):
277 278 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 279 280 query = u""" 281 282 SELECT DISTINCT ON (label) 283 atc_code, 284 label 285 FROM ( 286 287 SELECT 288 code as atc_code, 289 (code || ': ' || term || coalesce(' (' || ddd || unit || ')', '')) 290 AS label 291 FROM ref.atc 292 WHERE 293 term %(fragment_condition)s 294 OR 295 code %(fragment_condition)s 296 297 UNION ALL 298 299 SELECT 300 atc_code, 301 (atc_code || ': ' || description) 302 AS label 303 FROM ref.consumable_substance 304 WHERE 305 description %(fragment_condition)s 306 OR 307 atc_code %(fragment_condition)s 308 309 UNION ALL 310 311 SELECT 312 atc_code, 313 (atc_code || ': ' || description || ' (' || preparation || ')') 314 AS label 315 FROM ref.branded_drug 316 WHERE 317 description %(fragment_condition)s 318 OR 319 atc_code %(fragment_condition)s 320 321 -- it would be nice to be able to include clin.vacc_indication but that's hard to do in SQL 322 323 ) AS candidates 324 WHERE atc_code IS NOT NULL 325 ORDER BY label 326 LIMIT 50""" 327 328 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 329 mp.setThresholds(1, 2, 4) 330 # mp.word_separators = '[ \t=+&:@]+' 331 self.SetToolTipString(_('Select an ATC (Anatomical-Therapeutic-Chemical) code.')) 332 self.matcher = mp 333 self.selection_only = True
334 335 #============================================================ 336 # consumable substances widgets 337 #------------------------------------------------------------
338 -def manage_consumable_substances(parent=None):
339 340 if parent is None: 341 parent = wx.GetApp().GetTopWindow() 342 #------------------------------------------------------------ 343 def add_from_db(substance): 344 drug_db = get_drug_database(parent = parent) 345 if drug_db is None: 346 return False 347 drug_db.import_drugs() 348 return True
349 #------------------------------------------------------------ 350 def edit(substance=None): 351 return edit_consumable_substance(parent = parent, substance = substance, single_entry = (substance is not None)) 352 #------------------------------------------------------------ 353 def delete(substance): 354 if substance.is_in_use_by_patients: 355 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True) 356 return False 357 358 return gmMedication.delete_consumable_substance(substance = substance['pk']) 359 #------------------------------------------------------------ 360 def refresh(lctrl): 361 substs = gmMedication.get_consumable_substances(order_by = 'description') 362 items = [ [ 363 s['description'], 364 s['amount'], 365 s['unit'], 366 gmTools.coalesce(s['atc_code'], u''), 367 s['pk'] 368 ] for s in substs ] 369 lctrl.set_string_items(items) 370 lctrl.set_data(substs) 371 #------------------------------------------------------------ 372 msg = _('\nThese are the consumable substances registered with GNUmed.\n') 373 374 gmListWidgets.get_choices_from_list ( 375 parent = parent, 376 msg = msg, 377 caption = _('Showing consumable substances.'), 378 columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', u'#'], 379 single_selection = True, 380 new_callback = edit, 381 edit_callback = edit, 382 delete_callback = delete, 383 refresh_callback = refresh, 384 left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db) 385 ) 386 387 #------------------------------------------------------------
388 -def edit_consumable_substance(parent=None, substance=None, single_entry=False):
389 390 if substance is not None: 391 if substance.is_in_use_by_patients: 392 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True) 393 return False 394 395 ea = cConsumableSubstanceEAPnl(parent = parent, id = -1) 396 ea.data = substance 397 ea.mode = gmTools.coalesce(substance, 'new', 'edit') 398 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 399 dlg.SetTitle(gmTools.coalesce(substance, _('Adding new consumable substance'), _('Editing consumable substance'))) 400 if dlg.ShowModal() == wx.ID_OK: 401 dlg.Destroy() 402 return True 403 dlg.Destroy() 404 return False
405 406 #============================================================ 407 from Gnumed.wxGladeWidgets import wxgConsumableSubstanceEAPnl 408
409 -class cConsumableSubstanceEAPnl(wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl, gmEditArea.cGenericEditAreaMixin):
410
411 - def __init__(self, *args, **kwargs):
412 413 try: 414 data = kwargs['substance'] 415 del kwargs['substance'] 416 except KeyError: 417 data = None 418 419 wxgConsumableSubstanceEAPnl.wxgConsumableSubstanceEAPnl.__init__(self, *args, **kwargs) 420 gmEditArea.cGenericEditAreaMixin.__init__(self) 421 422 # Code using this mixin should set mode and data 423 # after instantiating the class: 424 self.mode = 'new' 425 self.data = data 426 if data is not None: 427 self.mode = 'edit'
428 429 # self.__init_ui() 430 #---------------------------------------------------------------- 431 # def __init_ui(self): 432 # self._PRW_atc.selection_only = False 433 #---------------------------------------------------------------- 434 # generic Edit Area mixin API 435 #----------------------------------------------------------------
436 - def _valid_for_save(self):
437 438 validity = True 439 440 if self._TCTRL_substance.GetValue().strip() == u'': 441 validity = False 442 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False) 443 self._TCTRL_substance.SetFocus() 444 else: 445 self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True) 446 447 try: 448 decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')) 449 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True) 450 except (TypeError, decimal.InvalidOperation): 451 validity = False 452 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False) 453 self._TCTRL_amount.SetFocus() 454 455 if self._PRW_unit.GetValue().strip() == u'': 456 validity = False 457 self._PRW_unit.display_as_valid(valid = False) 458 self._TCTRL_substance.SetFocus() 459 else: 460 self._PRW_unit.display_as_valid(valid = True) 461 462 if validity is False: 463 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. Missing essential input.')) 464 465 return validity
466 #----------------------------------------------------------------
467 - def _save_as_new(self):
468 subst = gmMedication.create_consumable_substance ( 469 substance = self._TCTRL_substance.GetValue().strip(), 470 atc = self._PRW_atc.GetData(), 471 amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')), 472 unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None)) 473 ) 474 success, data = subst.save() 475 if not success: 476 err, msg = data 477 _log.error(err) 478 _log.error(msg) 479 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True) 480 return False 481 482 self.data = subst 483 return True
484 #----------------------------------------------------------------
485 - def _save_as_update(self):
486 self.data['description'] = self._TCTRL_substance.GetValue().strip() 487 self.data['atc_code'] = self._PRW_atc.GetData() 488 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')) 489 self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function_initial = ('strip', None)) 490 success, data = self.data.save() 491 492 if not success: 493 err, msg = data 494 _log.error(err) 495 _log.error(msg) 496 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save consumable substance. %s') % msg, beep = True) 497 return False 498 499 return True
500 #----------------------------------------------------------------
501 - def _refresh_as_new(self):
502 self._TCTRL_substance.SetValue(u'') 503 self._TCTRL_amount.SetValue(u'') 504 self._PRW_unit.SetText(u'', None) 505 self._PRW_atc.SetText(u'', None) 506 507 self._TCTRL_substance.SetFocus()
508 #----------------------------------------------------------------
509 - def _refresh_from_existing(self):
510 self._TCTRL_substance.SetValue(self.data['description']) 511 self._TCTRL_amount.SetValue(u'%s' % self.data['amount']) 512 self._PRW_unit.SetText(self.data['unit'], self.data['unit']) 513 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc_code'], u''), self.data['atc_code']) 514 515 self._TCTRL_substance.SetFocus()
516 #----------------------------------------------------------------
518 self._refresh_as_new()
519 520 #============================================================ 521 # drug component widgets 522 #------------------------------------------------------------
523 -def manage_drug_components(parent=None):
524 525 if parent is None: 526 parent = wx.GetApp().GetTopWindow() 527 528 #------------------------------------------------------------ 529 def edit(component=None): 530 substance = gmMedication.cConsumableSubstance(aPK_obj = component['pk_consumable_substance']) 531 return edit_consumable_substance(parent = parent, substance = substance, single_entry = True)
532 #------------------------------------------------------------ 533 def delete(component): 534 if component.is_in_use_by_patients: 535 gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True) 536 return False 537 538 return component.containing_drug.remove_component(substance = component['pk_component']) 539 #------------------------------------------------------------ 540 def refresh(lctrl): 541 comps = gmMedication.get_drug_components() 542 items = [ [ 543 u'%s%s' % (c['brand'], gmTools.coalesce(c['atc_brand'], u'', u' [%s]')), 544 u'%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], u'', u' [%s]')), 545 u'%s %s' % (c['amount'], c['unit']), 546 c['preparation'], 547 gmTools.coalesce(c['external_code_brand'], u'', u'%%s [%s]' % c['external_code_type_brand']), 548 c['pk_component'] 549 ] for c in comps ] 550 lctrl.set_string_items(items) 551 lctrl.set_data(comps) 552 #------------------------------------------------------------ 553 msg = _('\nThese are the components in the drug brands known to GNUmed.\n') 554 555 gmListWidgets.get_choices_from_list ( 556 parent = parent, 557 msg = msg, 558 caption = _('Showing drug brand components.'), 559 columns = [_('Brand'), _('Substance'), _('Strength'), _('Preparation'), _('Code'), u'#'], 560 single_selection = True, 561 #new_callback = edit, 562 edit_callback = edit, 563 delete_callback = delete, 564 refresh_callback = refresh 565 ) 566 567 #------------------------------------------------------------
568 -def edit_drug_component(parent=None, drug_component=None, single_entry=False):
569 ea = cDrugComponentEAPnl(parent = parent, id = -1) 570 ea.data = drug_component 571 ea.mode = gmTools.coalesce(drug_component, 'new', 'edit') 572 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 573 dlg.SetTitle(gmTools.coalesce(drug_component, _('Adding new drug component'), _('Editing drug component'))) 574 if dlg.ShowModal() == wx.ID_OK: 575 dlg.Destroy() 576 return True 577 dlg.Destroy() 578 return False
579 580 #============================================================ 581 from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl 582
583 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
584
585 - def __init__(self, *args, **kwargs):
586 587 try: 588 data = kwargs['component'] 589 del kwargs['component'] 590 except KeyError: 591 data = None 592 593 wxgDrugComponentEAPnl.wxgDrugComponentEAPnl.__init__(self, *args, **kwargs) 594 gmEditArea.cGenericEditAreaMixin.__init__(self) 595 596 # Code using this mixin should set mode and data 597 # after instantiating the class: 598 self.mode = 'new' 599 self.data = data 600 if data is not None: 601 self.mode = 'edit'
602 603 #self.__init_ui() 604 #---------------------------------------------------------------- 605 # def __init_ui(self): 606 # # adjust phrasewheels etc 607 #---------------------------------------------------------------- 608 # generic Edit Area mixin API 609 #----------------------------------------------------------------
610 - def _valid_for_save(self):
611 if self.data is not None: 612 if self.data['is_in_use']: 613 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug component. It is in use.'), beep = True) 614 return False 615 616 validity = True 617 618 if self._PRW_substance.GetData() is None: 619 validity = False 620 self._PRW_substance.display_as_valid(False) 621 else: 622 self._PRW_substance.display_as_valid(True) 623 624 val = self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1) 625 try: 626 decimal.Decimal(val) 627 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True) 628 except: 629 validity = False 630 self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False) 631 632 if self._PRW_unit.GetValue().strip() == u'': 633 validity = False 634 self._PRW_unit.display_as_valid(False) 635 else: 636 self._PRW_unit.display_as_valid(True) 637 638 if validity is False: 639 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save drug component. Invalid or missing essential input.')) 640 641 return validity
642 #----------------------------------------------------------------
643 - def _save_as_new(self):
644 # save the data as a new instance 645 data = 1 646 data[''] = 1 647 data[''] = 1 648 # data.save() 649 650 # must be done very late or else the property access 651 # will refresh the display such that later field 652 # access will return empty values 653 # self.data = data 654 return False 655 return True
656 #----------------------------------------------------------------
657 - def _save_as_update(self):
658 self.data['pk_consumable_substance'] = self._PRW_substance.GetData() 659 self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', u'.', 1)) 660 self.data['unit'] = self._PRW_unit.GetValue().strip() 661 return self.data.save()
662 #----------------------------------------------------------------
663 - def _refresh_as_new(self):
664 self._TCTRL_brand.SetValue(u'') 665 self._TCTRL_components.SetValue(u'') 666 self._TCTRL_codes.SetValue(u'') 667 self._PRW_substance.SetText(u'', None) 668 self._TCTRL_amount.SetValue(u'') 669 self._PRW_unit.SetText(u'', None) 670 671 self._PRW_substance.SetFocus()
672 #----------------------------------------------------------------
673 - def _refresh_from_existing(self):
674 self._TCTRL_brand.SetValue(u'%s (%s)' % (self.data['brand'], self.data['preparation'])) 675 self._TCTRL_components.SetValue(u' / '.join(self.data.containing_drug['components'])) 676 details = [] 677 if self.data['atc_brand'] is not None: 678 details.append(u'ATC: %s' % self.data['atc_brand']) 679 if self.data['external_code_brand'] is not None: 680 details.append(u'%s: %s' % (self.data['external_code_type_brand'], self.data['external_code_brand'])) 681 self._TCTRL_codes.SetValue(u'; '.join(details)) 682 683 self._PRW_substance.SetText(self.data['substance'], self.data['pk_consumable_substance']) 684 self._TCTRL_amount.SetValue(u'%s' % self.data['amount']) 685 self._PRW_unit.SetText(self.data['unit'], self.data['unit']) 686 687 self._PRW_substance.SetFocus()
688 #----------------------------------------------------------------
690 #self._PRW_brand.SetText(u'', None) 691 #self._TCTRL_prep.SetValue(u'') 692 #self._TCTRL_brand_details.SetValue(u'') 693 self._PRW_substance.SetText(u'', None) 694 self._TCTRL_amount.SetValue(u'') 695 self._PRW_unit.SetText(u'', None) 696 697 self._PRW_substance.SetFocus()
698 699 #============================================================
700 -class cDrugComponentPhraseWheel(gmPhraseWheel.cPhraseWheel):
701
702 - def __init__(self, *args, **kwargs):
703 704 mp = gmMedication.cDrugComponentMatchProvider() 705 mp.setThresholds(2, 3, 4) 706 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 707 self.SetToolTipString(_('A drug component with optional strength.')) 708 self.matcher = mp 709 self.selection_only = False
710 #--------------------------------------------------------
711 - def _data2instance(self):
712 return gmMedication.cDrugComponent(aPK_obj = self.GetData(as_instance = False, can_create = False))
713 #============================================================ 714 #============================================================
715 -class cSubstancePreparationPhraseWheel(gmPhraseWheel.cPhraseWheel):
716
717 - def __init__(self, *args, **kwargs):
718 719 query = u""" 720 ( 721 SELECT DISTINCT ON (preparation) 722 preparation as prep, preparation 723 FROM ref.branded_drug 724 WHERE preparation %(fragment_condition)s 725 ) UNION ( 726 SELECT DISTINCT ON (preparation) 727 preparation as prep, preparation 728 FROM clin.substance_intake 729 WHERE preparation %(fragment_condition)s 730 ) 731 ORDER BY prep 732 limit 30""" 733 734 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 735 mp.setThresholds(1, 2, 4) 736 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 737 self.SetToolTipString(_('The preparation (form) of the substance or brand.')) 738 self.matcher = mp 739 self.selection_only = False
740 #============================================================
741 -class cSubstancePhraseWheel(gmPhraseWheel.cPhraseWheel):
742
743 - def __init__(self, *args, **kwargs):
744 745 mp = gmMedication.cSubstanceMatchProvider() 746 mp.setThresholds(1, 2, 4) 747 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 748 self.SetToolTipString(_('The substance with optional strength.')) 749 self.matcher = mp 750 self.selection_only = False 751 self.phrase_separators = None
752 753 #--------------------------------------------------------
754 - def _data2instance(self):
755 return gmMedication.cConsumableSubstance(aPK_obj = self.GetData(as_instance = False, can_create = False))
756 #============================================================ 757 # branded drugs widgets 758 #------------------------------------------------------------
759 -def manage_components_of_branded_drug(parent=None, brand=None):
760 761 if brand is not None: 762 if brand.is_in_use_by_patients: 763 gmGuiHelpers.gm_show_info ( 764 aTitle = _('Managing components of a drug'), 765 aMessage = _( 766 'Cannot manage the components of the branded drug product\n' 767 '\n' 768 ' "%s" (%s)\n' 769 '\n' 770 'because it is currently taken by patients.\n' 771 ) % (brand['brand'], brand['preparation']) 772 ) 773 return False 774 #-------------------------------------------------------- 775 if parent is None: 776 parent = wx.GetApp().GetTopWindow() 777 #-------------------------------------------------------- 778 # def manage_substances(): 779 # pass 780 #-------------------------------------------------------- 781 if brand is None: 782 msg = _('Pick the substances which are components of this drug.') 783 right_col = _('Components of drug') 784 comp_substs = [] 785 else: 786 right_col = u'%s (%s)' % (brand['brand'], brand['preparation']) 787 msg = _( 788 'Adjust the components of "%s"\n' 789 '\n' 790 'The drug must contain at least one component. Any given\n' 791 'substance can only be included once per drug.' 792 ) % right_col 793 comp_substs = [ c.substance for c in brand.components ] 794 795 substs = gmMedication.get_consumable_substances(order_by = 'description') 796 choices = [ u'%s %s %s' % (s['description'], s['amount'], s['unit']) for s in substs ] 797 picks = [ u'%s %s %s' % (c['description'], c['amount'], c['unit']) for c in comp_substs ] 798 799 picker = gmListWidgets.cItemPickerDlg ( 800 parent, 801 -1, 802 title = _('Managing components of a drug ...'), 803 msg = msg 804 ) 805 picker.set_columns(['Substances'], [right_col]) 806 picker.set_choices(choices = choices, data = substs) 807 picker.set_picks(picks = picks, data = comp_substs) 808 # picker.extra_button = ( 809 # _('Substances'), 810 # _('Manage list of consumable substances'), 811 # manage_substances 812 # ) 813 814 btn_pressed = picker.ShowModal() 815 substs = picker.get_picks() 816 picker.Destroy() 817 818 if btn_pressed != wx.ID_OK: 819 return (False, None) 820 821 if brand is not None: 822 brand.set_substances_as_components(substances = substs) 823 824 return (True, substs)
825 #------------------------------------------------------------
826 -def manage_branded_drugs(parent=None, ignore_OK_button=False):
827 828 if parent is None: 829 parent = wx.GetApp().GetTopWindow() 830 #------------------------------------------------------------ 831 def add_from_db(brand): 832 drug_db = get_drug_database(parent = parent) 833 if drug_db is None: 834 return False 835 drug_db.import_drugs() 836 return True
837 #------------------------------------------------------------ 838 def get_tooltip(brand=None): 839 tt = u'%s %s\n' % (brand['brand'], brand['preparation']) 840 tt += u'\n' 841 tt += u'%s%s%s\n' % ( 842 gmTools.bool2subst(brand.is_vaccine, u'%s, ' % _('Vaccine'), u''), 843 u'%s, ' % gmTools.bool2subst(brand.is_in_use_by_patients, _('in use'), _('not in use')), 844 gmTools.bool2subst(brand['is_fake_brand'], _('fake'), u'') 845 ) 846 tt += gmTools.coalesce(brand['atc'], u'', _('ATC: %s\n')) 847 tt += gmTools.coalesce(brand['external_code'], u'', u'%s: %%s\n' % brand['external_code_type']) 848 if brand['components'] is not None: 849 tt += u'- %s' % u'\n- '.join(brand['components']) 850 return tt 851 #------------------------------------------------------------ 852 def edit(brand): 853 if brand is not None: 854 if brand.is_vaccine: 855 gmGuiHelpers.gm_show_info ( 856 aTitle = _('Editing medication'), 857 aMessage = _( 858 'Cannot edit the medication\n' 859 '\n' 860 ' "%s" (%s)\n' 861 '\n' 862 'because it is a vaccine. Please edit it\n' 863 'from the vaccine management section !\n' 864 ) % (brand['brand'], brand['preparation']) 865 ) 866 return False 867 868 return edit_branded_drug(parent = parent, branded_drug = brand, single_entry = True) 869 #------------------------------------------------------------ 870 def delete(brand): 871 if brand.is_vaccine: 872 gmGuiHelpers.gm_show_info ( 873 aTitle = _('Deleting medication'), 874 aMessage = _( 875 'Cannot delete the medication\n' 876 '\n' 877 ' "%s" (%s)\n' 878 '\n' 879 'because it is a vaccine. Please delete it\n' 880 'from the vaccine management section !\n' 881 ) % (brand['brand'], brand['preparation']) 882 ) 883 return False 884 gmMedication.delete_branded_drug(brand = brand['pk_brand']) 885 return True 886 #------------------------------------------------------------ 887 def new(): 888 return edit_branded_drug(parent = parent, branded_drug = None, single_entry = False) 889 #------------------------------------------------------------ 890 def refresh(lctrl): 891 drugs = gmMedication.get_branded_drugs() 892 items = [ [ 893 u'%s%s' % ( 894 d['brand'], 895 gmTools.bool2subst(d['is_fake_brand'], ' (%s)' % _('fake'), u'') 896 ), 897 d['preparation'], 898 gmTools.coalesce(d['atc'], u''), 899 gmTools.coalesce(d['components'], u''), 900 gmTools.coalesce(d['external_code'], u'', u'%%s [%s]' % d['external_code_type']), 901 d['pk_brand'] 902 ] for d in drugs ] 903 lctrl.set_string_items(items) 904 lctrl.set_data(drugs) 905 #------------------------------------------------------------ 906 msg = _('\nThese are the drug brands known to GNUmed.\n') 907 908 gmListWidgets.get_choices_from_list ( 909 parent = parent, 910 msg = msg, 911 caption = _('Showing branded drugs.'), 912 columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), u'#'], 913 single_selection = True, 914 ignore_OK_button = ignore_OK_button, 915 refresh_callback = refresh, 916 new_callback = new, 917 edit_callback = edit, 918 delete_callback = delete, 919 list_tooltip_callback = get_tooltip, 920 left_extra_button = (_('Import'), _('Import substances and brands from a drug database.'), add_from_db) 921 #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing) 922 #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients) 923 ) 924 925 #------------------------------------------------------------
926 -def edit_branded_drug(parent=None, branded_drug=None, single_entry=False):
927 928 if branded_drug is not None: 929 if branded_drug.is_in_use_by_patients: 930 gmGuiHelpers.gm_show_info ( 931 aTitle = _('Editing drug'), 932 aMessage = _( 933 'Cannot edit the branded drug product\n' 934 '\n' 935 ' "%s" (%s)\n' 936 '\n' 937 'because it is currently taken by patients.\n' 938 ) % (branded_drug['brand'], branded_drug['preparation']) 939 ) 940 return False 941 942 if parent is None: 943 parent = wx.GetApp().GetTopWindow() 944 #-------------------------------------------- 945 def manage_substances(drug): 946 manage_consumable_substances(parent = parent)
947 #-------------------------------------------- 948 ea = cBrandedDrugEAPnl(parent = parent, id = -1) 949 ea.data = branded_drug 950 ea.mode = gmTools.coalesce(branded_drug, 'new', 'edit') 951 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 952 dlg.SetTitle(gmTools.coalesce(branded_drug, _('Adding new drug brand'), _('Editing drug brand'))) 953 dlg.left_extra_button = ( 954 _('Substances'), 955 _('Manage consumable substances'), 956 manage_substances 957 ) 958 if dlg.ShowModal() == wx.ID_OK: 959 dlg.Destroy() 960 return True 961 dlg.Destroy() 962 return False 963 964 #============================================================ 965 from Gnumed.wxGladeWidgets import wxgBrandedDrugEAPnl 966
967 -class cBrandedDrugEAPnl(wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
968
969 - def __init__(self, *args, **kwargs):
970 971 try: 972 data = kwargs['drug'] 973 del kwargs['drug'] 974 except KeyError: 975 data = None 976 977 wxgBrandedDrugEAPnl.wxgBrandedDrugEAPnl.__init__(self, *args, **kwargs) 978 gmEditArea.cGenericEditAreaMixin.__init__(self) 979 980 self.mode = 'new' 981 self.data = data 982 if data is not None: 983 self.mode = 'edit' 984 self.__component_substances = data.components_as_substances
985 986 #self.__init_ui() 987 #---------------------------------------------------------------- 988 # def __init_ui(self): 989 # adjust external type PRW 990 #---------------------------------------------------------------- 991 # generic Edit Area mixin API 992 #----------------------------------------------------------------
993 - def _valid_for_save(self):
994 995 if self.data is not None: 996 if self.data.is_in_use_by_patients: 997 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit drug brand. It is in use.'), beep = True) 998 return False 999 1000 validity = True 1001 1002 if self._PRW_brand.GetValue().strip() == u'': 1003 validity = False 1004 self._PRW_brand.display_as_valid(False) 1005 else: 1006 self._PRW_brand.display_as_valid(True) 1007 1008 if self._PRW_preparation.GetValue().strip() == u'': 1009 validity = False 1010 self._PRW_preparation.display_as_valid(False) 1011 else: 1012 self._PRW_preparation.display_as_valid(True) 1013 1014 if validity is True: 1015 self._TCTRL_components.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND)) 1016 if len(self.__component_substances) == 0: 1017 wants_empty = gmGuiHelpers.gm_show_question ( 1018 title = _('Checking brand data'), 1019 question = _( 1020 'You have not selected any substances\n' 1021 'as drug components.\n' 1022 '\n' 1023 'Without components you will not be able to\n' 1024 'use this drug for documenting patient care.\n' 1025 '\n' 1026 'Are you sure you want to save\n' 1027 'it without components ?' 1028 ) 1029 ) 1030 if not wants_empty: 1031 validity = False 1032 self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False) 1033 1034 if validity is False: 1035 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save branded drug. Invalid or missing essential input.')) 1036 1037 return validity
1038 #----------------------------------------------------------------
1039 - def _save_as_new(self):
1040 1041 drug = gmMedication.create_branded_drug ( 1042 brand_name = self._PRW_brand.GetValue().strip(), 1043 preparation = gmTools.coalesce ( 1044 self._PRW_preparation.GetData(), 1045 self._PRW_preparation.GetValue() 1046 ).strip(), 1047 return_existing = True 1048 ) 1049 drug['is_fake_brand'] = self._CHBOX_is_fake.GetValue() 1050 drug['atc'] = self._PRW_atc.GetData() 1051 code = self._TCTRL_external_code.GetValue().strip() 1052 if code != u'': 1053 drug['external_code'] = code 1054 drug['external_code_type'] = self._PRW_external_code_type.GetData().strip() 1055 1056 drug.save() 1057 1058 if len(self.__component_substances) > 0: 1059 drug.set_substances_as_components(substances = self.__component_substances) 1060 1061 self.data = drug 1062 1063 return True
1064 #----------------------------------------------------------------
1065 - def _save_as_update(self):
1066 self.data['brand'] = self._PRW_brand.GetValue().strip() 1067 self.data['preparation'] = gmTools.coalesce ( 1068 self._PRW_preparation.GetData(), 1069 self._PRW_preparation.GetValue() 1070 ).strip() 1071 self.data['is_fake_brand'] = self._CHBOX_is_fake.GetValue() 1072 self.data['atc'] = self._PRW_atc.GetData() 1073 code = self._TCTRL_external_code.GetValue().strip() 1074 if code != u'': 1075 self.data['external_code'] = code 1076 self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip() 1077 success, data = self.data.save() 1078 if not success: 1079 err, msg = data 1080 _log.error('problem saving') 1081 _log.error('%s', err) 1082 _log.error('%s', msg) 1083 return (success is True)
1084 #----------------------------------------------------------------
1085 - def _refresh_as_new(self):
1086 self._PRW_brand.SetText(u'', None) 1087 self._PRW_preparation.SetText(u'', None) 1088 self._CHBOX_is_fake.SetValue(False) 1089 self._TCTRL_components.SetValue(u'') 1090 self._PRW_atc.SetText(u'', None) 1091 self._TCTRL_external_code.SetValue(u'') 1092 self._PRW_external_code_type.SetText(u'', None) 1093 1094 self._PRW_brand.SetFocus() 1095 1096 self.__component_substances = []
1097 #----------------------------------------------------------------
1099 self._refresh_as_new()
1100 #----------------------------------------------------------------
1101 - def _refresh_from_existing(self):
1102 self._PRW_brand.SetText(self.data['brand'], self.data['pk_brand']) 1103 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation']) 1104 self._CHBOX_is_fake.SetValue(self.data['is_fake_brand']) 1105 comps = u'' 1106 if self.data['components'] is not None: 1107 comps = u'- %s' % u'\n- '.join(self.data['components']) 1108 self._TCTRL_components.SetValue(comps) 1109 self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], u''), self.data['atc']) 1110 self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], u'')) 1111 t = gmTools.coalesce(self.data['external_code_type'], u'') 1112 self._PRW_external_code_type.SetText(t, t) 1113 1114 self._PRW_brand.SetFocus() 1115 1116 self.__component_substances = self.data.components_as_substances
1117 #---------------------------------------------------------------- 1118 # event handler 1119 #----------------------------------------------------------------
1120 - def _on_manage_components_button_pressed(self, event):
1121 event.Skip() 1122 if self.mode == 'new_from_existing': 1123 brand = None 1124 else: 1125 brand = self.data 1126 OKed, substs = manage_components_of_branded_drug(parent = self, brand = brand) 1127 if OKed is True: 1128 self.__component_substances = substs 1129 comps = u'' 1130 if len(substs) > 0: 1131 comps = u'- %s' % u'\n- '.join([ u'%s %s %s' % (s['description'], s['amount'], s['unit']) for s in substs ]) 1132 self._TCTRL_components.SetValue(comps)
1133 #============================================================
1134 -class cBrandedDrugPhraseWheel(gmPhraseWheel.cPhraseWheel):
1135
1136 - def __init__(self, *args, **kwargs):
1137 1138 query = u""" 1139 SELECT 1140 pk 1141 AS data, 1142 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', '')) 1143 AS list_label, 1144 (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', '')) 1145 AS field_label 1146 FROM ref.branded_drug 1147 WHERE description %(fragment_condition)s 1148 ORDER BY list_label 1149 LIMIT 50""" 1150 1151 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 1152 mp.setThresholds(2, 3, 4) 1153 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1154 self.SetToolTipString(_( 1155 'The brand name of the drug.\n' 1156 '\n' 1157 'Note: a brand name will need to be linked to\n' 1158 'one or more components before it can be used,\n' 1159 'except in the case of fake (generic) vaccines.' 1160 )) 1161 self.matcher = mp 1162 self.selection_only = False
1163 1164 #============================================================ 1165 # current substance intake widgets 1166 #------------------------------------------------------------
1167 -class cSubstanceSchedulePhraseWheel(gmPhraseWheel.cPhraseWheel):
1168
1169 - def __init__(self, *args, **kwargs):
1170 1171 query = u""" 1172 SELECT DISTINCT ON (sched) 1173 schedule as sched, 1174 schedule 1175 FROM clin.substance_intake 1176 WHERE schedule %(fragment_condition)s 1177 ORDER BY sched 1178 LIMIT 50""" 1179 1180 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 1181 mp.setThresholds(1, 2, 4) 1182 mp.word_separators = '[ \t=+&:@]+' 1183 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1184 self.SetToolTipString(_('The schedule for taking this substance.')) 1185 self.matcher = mp 1186 self.selection_only = False
1187 #============================================================
1188 -def turn_substance_intake_into_allergy(parent=None, intake=None, emr=None):
1189 1190 if intake['is_currently_active']: 1191 intake['discontinued'] = gmDateTime.pydt_now_here() 1192 if intake['discontinue_reason'] is None: 1193 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance')) 1194 else: 1195 if not intake['discontinue_reason'].startswith(_('not tolerated:')): 1196 intake['discontinue_reason'] = u'%s %s' % (_('not tolerated:'), intake['discontinue_reason']) 1197 if not intake.save(): 1198 return False 1199 1200 allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter']) 1201 1202 brand = intake.containing_drug 1203 if brand is not None: 1204 comps = [ c['substance'] for c in brand.components ] 1205 if len(comps) > 1: 1206 gmGuiHelpers.gm_show_info ( 1207 aTitle = _(u'Documented an allergy'), 1208 aMessage = _( 1209 u'An allergy was documented against the substance:\n' 1210 u'\n' 1211 u' [%s]\n' 1212 u'\n' 1213 u'This substance was taken with the multi-component brand:\n' 1214 u'\n' 1215 u' [%s (%s)]\n' 1216 u'\n' 1217 u'Note that ALL components of this brand were discontinued.' 1218 ) % ( 1219 intake['substance'], 1220 intake['brand'], 1221 u' & '.join(comps) 1222 ) 1223 ) 1224 1225 if parent is None: 1226 parent = wx.GetApp().GetTopWindow() 1227 1228 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent = parent, id = -1) 1229 dlg.ShowModal() 1230 1231 return True
1232 #============================================================ 1233 from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl 1234
1235 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
1236
1237 - def __init__(self, *args, **kwargs):
1238 1239 try: 1240 data = kwargs['substance'] 1241 del kwargs['substance'] 1242 except KeyError: 1243 data = None 1244 1245 wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs) 1246 gmEditArea.cGenericEditAreaMixin.__init__(self) 1247 1248 self.mode = 'new' 1249 self.data = data 1250 if data is not None: 1251 self.mode = 'edit' 1252 1253 self.__init_ui()
1254 #----------------------------------------------------------------
1255 - def __init_ui(self):
1256 1257 self._PRW_component.add_callback_on_lose_focus(callback = self._on_leave_component) 1258 self._PRW_component.selection_only = True 1259 1260 self._PRW_substance.add_callback_on_lose_focus(callback = self._on_leave_substance) 1261 self._PRW_substance.selection_only = True
1262 #----------------------------------------------------------------
1263 - def __refresh_allergies(self):
1264 emr = gmPerson.gmCurrentPatient().get_emr() 1265 1266 state = emr.allergy_state 1267 if state['last_confirmed'] is None: 1268 confirmed = _('never') 1269 else: 1270 confirmed = state['last_confirmed'].strftime('%Y %B %d').decode(gmI18N.get_encoding()) 1271 msg = _(u'%s, last confirmed %s\n') % (state.state_string, confirmed) 1272 msg += gmTools.coalesce(state['comment'], u'', _('Comment (%s): %%s\n') % state['modified_by']) 1273 msg += u'\n' 1274 1275 for allergy in emr.get_allergies(): 1276 msg += u'%s (%s, %s): %s\n' % ( 1277 allergy['descriptor'], 1278 allergy['l10n_type'], 1279 gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), u'?'), 1280 gmTools.coalesce(allergy['reaction'], _('reaction not recorded')) 1281 ) 1282 1283 self._LBL_allergies.SetLabel(msg)
1284 #---------------------------------------------------------------- 1285 # generic Edit Area mixin API 1286 #----------------------------------------------------------------
1287 - def _valid_for_save(self):
1288 1289 validity = True 1290 1291 has_component = (self._PRW_component.GetData() is not None) 1292 has_substance = (self._PRW_substance.GetValue().strip() != u'') 1293 1294 self._PRW_component.display_as_valid(True) 1295 1296 # cannot add duplicate components 1297 if self.mode == 'new': 1298 msg = _( 1299 'The patient is already taking\n' 1300 '\n' 1301 ' %s\n' 1302 '\n' 1303 'You will want to adjust the schedule\n' 1304 'rather than document the intake twice.' 1305 ) 1306 title = _('Adding substance intake entry') 1307 if has_component: 1308 emr = gmPerson.gmCurrentPatient().get_emr() 1309 if emr.substance_intake_exists(pk_component = self._PRW_component.GetData()): 1310 gmGuiHelpers.gm_show_warning ( 1311 aTitle = title, 1312 aMessage = msg % self._PRW_component.GetValue().strip() 1313 ) 1314 self._PRW_component.display_as_valid(False) 1315 validity = False 1316 pk_substance = self._PRW_substance.GetData() 1317 if pk_substance is not None: 1318 emr = gmPerson.gmCurrentPatient().get_emr() 1319 if emr.substance_intake_exists(pk_substance = pk_substance): 1320 gmGuiHelpers.gm_show_warning ( 1321 aTitle = title, 1322 aMessage = msg % self._PRW_substance.GetValue().strip() 1323 ) 1324 self._PRW_substance.display_as_valid(False) 1325 validity = False 1326 1327 # must have either brand or substance 1328 if (has_component is False) and (has_substance is False): 1329 self._PRW_substance.display_as_valid(False) 1330 self._PRW_component.display_as_valid(False) 1331 validity = False 1332 else: 1333 self._PRW_substance.display_as_valid(True) 1334 1335 # brands already have a preparation, so only required for substances 1336 if not has_component: 1337 if self._PRW_preparation.GetValue().strip() == u'': 1338 self._PRW_preparation.display_as_valid(False) 1339 validity = False 1340 else: 1341 self._PRW_preparation.display_as_valid(True) 1342 1343 # episode must be set if intake is to be approved of 1344 if self._CHBOX_approved.IsChecked(): 1345 if self._PRW_episode.GetValue().strip() == u'': 1346 self._PRW_episode.display_as_valid(False) 1347 validity = False 1348 else: 1349 self._PRW_episode.display_as_valid(True) 1350 1351 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 1352 self._PRW_duration.display_as_valid(True) 1353 else: 1354 if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None: 1355 self._PRW_duration.display_as_valid(False) 1356 validity = False 1357 else: 1358 self._PRW_duration.display_as_valid(True) 1359 1360 # end must be > start if at all 1361 end = self._DP_discontinued.GetData() 1362 if end is not None: 1363 start = self._DP_started.GetData() 1364 if start > end: 1365 self._DP_started.display_as_valid(False) 1366 self._DP_discontinued.display_as_valid(False) 1367 validity = False 1368 else: 1369 self._DP_started.display_as_valid(True) 1370 self._DP_discontinued.display_as_valid(True) 1371 1372 if validity is False: 1373 gmDispatcher.send(signal = 'statustext', msg = _('Input incomplete/invalid for saving as substance intake.')) 1374 1375 return validity
1376 #----------------------------------------------------------------
1377 - def _save_as_new(self):
1378 1379 emr = gmPerson.gmCurrentPatient().get_emr() 1380 epi = self._PRW_episode.GetData(can_create = True) 1381 1382 if self._PRW_substance.GetData() is None: 1383 # auto-creates all components as intakes 1384 intake = emr.add_substance_intake ( 1385 pk_component = self._PRW_component.GetData(), 1386 episode = epi 1387 ) 1388 else: 1389 intake = emr.add_substance_intake ( 1390 pk_substance = self._PRW_substance.GetData(), 1391 episode = epi, 1392 preparation = self._PRW_preparation.GetValue().strip() 1393 ) 1394 1395 if intake is None: 1396 gmDispatcher.send('statustext', msg = _('Cannot add duplicate of (maybe inactive) substance intake.'), beep = True) 1397 return False 1398 1399 intake['started'] = self._DP_started.GetData() 1400 intake['discontinued'] = self._DP_discontinued.GetData() 1401 if intake['discontinued'] is None: 1402 intake['discontinue_reason'] = None 1403 else: 1404 intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip() 1405 intake['schedule'] = self._PRW_schedule.GetValue().strip() 1406 intake['aim'] = self._PRW_aim.GetValue().strip() 1407 intake['notes'] = self._PRW_notes.GetValue().strip() 1408 intake['is_long_term'] = self._CHBOX_long_term.IsChecked() 1409 intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 1410 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 1411 intake['duration'] = None 1412 else: 1413 intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 1414 intake.save() 1415 1416 self.data = intake 1417 1418 return True
1419 #----------------------------------------------------------------
1420 - def _save_as_update(self):
1421 1422 # auto-applies to all components of drug if any: 1423 self.data['started'] = self._DP_started.GetData() 1424 self.data['discontinued'] = self._DP_discontinued.GetData() 1425 if self.data['discontinued'] is None: 1426 self.data['discontinue_reason'] = None 1427 else: 1428 self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip() 1429 self.data['schedule'] = self._PRW_schedule.GetValue() 1430 self.data['is_long_term'] = self._CHBOX_long_term.IsChecked() 1431 self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked() 1432 if self._PRW_duration.GetValue().strip() in [u'', gmTools.u_infinity]: 1433 self.data['duration'] = None 1434 else: 1435 self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue()) 1436 1437 # applies to non-component substances only 1438 self.data['preparation'] = self._PRW_preparation.GetValue() 1439 1440 # per-component 1441 self.data['aim'] = self._PRW_aim.GetValue() 1442 self.data['notes'] = self._PRW_notes.GetValue() 1443 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True) 1444 1445 self.data.save() 1446 1447 return True
1448 #----------------------------------------------------------------
1449 - def _refresh_as_new(self):
1450 self._PRW_component.SetText(u'', None) 1451 self._LBL_component.Enable(True) 1452 self._PRW_component.Enable(True) 1453 self._TCTRL_brand_ingredients.SetValue(u'') 1454 self._TCTRL_brand_ingredients.SetToolTipString(u'') 1455 1456 self._LBL_or.Enable(True) 1457 1458 self._PRW_substance.SetText(u'', None) 1459 self._PRW_substance.Enable(True) 1460 1461 self._PRW_preparation.SetText(u'', None) 1462 self._PRW_preparation.Enable(True) 1463 1464 self._PRW_schedule.SetText(u'', None) 1465 self._PRW_duration.SetText(u'', None) 1466 self._PRW_aim.SetText(u'', None) 1467 self._PRW_notes.SetText(u'', None) 1468 self._PRW_episode.SetText(u'', None) 1469 1470 self._CHBOX_long_term.SetValue(False) 1471 self._CHBOX_approved.SetValue(True) 1472 1473 self._DP_started.SetData(gmDateTime.pydt_now_here()) 1474 self._DP_discontinued.SetData(None) 1475 self._PRW_discontinue_reason.SetValue(u'') 1476 1477 self.__refresh_allergies() 1478 1479 self._PRW_component.SetFocus()
1480 #----------------------------------------------------------------
1481 - def _refresh_from_existing(self):
1482 1483 self._TCTRL_brand_ingredients.SetValue(u'') 1484 self._TCTRL_brand_ingredients.SetToolTipString(u'') 1485 1486 if self.data['pk_brand'] is None: 1487 self.__refresh_from_existing_substance() 1488 else: 1489 self.__refresh_from_existing_component() 1490 1491 # no editing of substance or component 1492 self._LBL_component.Enable(False) 1493 self._PRW_component.Enable(False) 1494 self._LBL_or.Enable(False) 1495 self._PRW_substance.Enable(False) 1496 1497 if self.data['is_long_term']: 1498 self._CHBOX_long_term.SetValue(True) 1499 self._PRW_duration.Enable(False) 1500 self._PRW_duration.SetText(gmTools.u_infinity, None) 1501 self._BTN_discontinued_as_planned.Enable(False) 1502 else: 1503 self._CHBOX_long_term.SetValue(False) 1504 self._PRW_duration.Enable(True) 1505 self._BTN_discontinued_as_planned.Enable(True) 1506 if self.data['duration'] is None: 1507 self._PRW_duration.SetText(u'', None) 1508 else: 1509 self._PRW_duration.SetText(gmDateTime.format_interval(self.data['duration'], gmDateTime.acc_days), self.data['duration']) 1510 self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], u''), self.data['aim']) 1511 self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], u''), self.data['notes']) 1512 self._PRW_episode.SetData(self.data['pk_episode']) 1513 self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], u''), self.data['schedule']) 1514 1515 self._CHBOX_approved.SetValue(self.data['intake_is_approved_of']) 1516 1517 self._DP_started.SetData(self.data['started']) 1518 self._DP_discontinued.SetData(self.data['discontinued']) 1519 self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], u'')) 1520 if self.data['discontinued'] is not None: 1521 self._PRW_discontinue_reason.Enable() 1522 1523 self.__refresh_allergies() 1524 1525 self._PRW_schedule.SetFocus()
1526 #----------------------------------------------------------------
1528 self._LBL_component.Enable(False) 1529 self._PRW_component.Enable(False) 1530 self._PRW_component.SetText(u'', None) 1531 self._PRW_component.display_as_valid(True) 1532 1533 self._LBL_or.Enable(False) 1534 1535 self._PRW_substance.Enable(True) 1536 self._PRW_substance.SetText ( 1537 u'%s %s %s' % (self.data['substance'], self.data['amount'], self.data['unit']), 1538 self.data['pk_substance'] 1539 ) 1540 1541 self._PRW_preparation.SetText(gmTools.coalesce(self.data['preparation'], u''), self.data['preparation']) 1542 self._PRW_preparation.Enable(True)
1543 #----------------------------------------------------------------
1545 self._LBL_component.Enable(True) 1546 self._PRW_component.Enable(True) 1547 self._PRW_component.SetText ( 1548 u'%s %s %s (%s)' % (self.data['substance'], self.data['amount'], self.data['unit'], self.data['brand']), 1549 self.data['pk_drug_component'] 1550 ) 1551 1552 brand = gmMedication.cBrandedDrug(aPK_obj = self.data['pk_brand']) 1553 if brand['components'] is not None: 1554 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components'])) 1555 tt = u'%s:\n\n- %s' % ( 1556 self.data['brand'], 1557 u'\n- '.join(brand['components']) 1558 ) 1559 self._TCTRL_brand_ingredients.SetToolTipString(tt) 1560 1561 self._LBL_or.Enable(False) 1562 self._LBL_substance.Enable(False) 1563 self._PRW_substance.SetText(u'', None) 1564 self._PRW_substance.display_as_valid(True) 1565 1566 self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation']) 1567 self._PRW_preparation.Enable(False)
1568 #----------------------------------------------------------------
1570 self._refresh_as_new()
1571 #---------------------------------------------------------------- 1572 # event handlers 1573 #----------------------------------------------------------------
1574 - def _on_leave_component(self):
1575 if self._PRW_component.GetData() is None: 1576 self._LBL_or.Enable(True) 1577 self._PRW_component.SetText(u'', None) 1578 self._LBL_substance.Enable(True) 1579 self._PRW_substance.Enable(True) 1580 self._LBL_preparation.Enable(True) 1581 self._PRW_preparation.Enable(True) 1582 self._PRW_preparation.SetText(u'', None) 1583 self._TCTRL_brand_ingredients.SetValue(u'') 1584 self._TCTRL_brand_ingredients.SetToolTipString(u'') 1585 else: 1586 self._LBL_or.Enable(False) 1587 self._LBL_substance.Enable(False) 1588 self._PRW_substance.SetText(u'', None) 1589 self._PRW_substance.display_as_valid(True) 1590 self._PRW_substance.Enable(False) 1591 self._LBL_preparation.Enable(False) 1592 self._PRW_preparation.Enable(False) 1593 comp = gmMedication.cDrugComponent(aPK_obj = self._PRW_component.GetData()) 1594 self._PRW_preparation.SetText(comp['preparation'], comp['preparation']) 1595 brand = comp.containing_drug 1596 if brand['components'] is not None: 1597 self._TCTRL_brand_ingredients.SetValue(u'; '.join(brand['components'])) 1598 tt = u'%s:\n\n- %s' % ( 1599 brand['brand'], 1600 u'\n- '.join(brand['components']) 1601 ) 1602 self._TCTRL_brand_ingredients.SetToolTipString(tt)
1603 #----------------------------------------------------------------
1604 - def _on_leave_substance(self):
1605 if self._PRW_substance.GetData() is None: 1606 self._LBL_or.Enable(True) 1607 self._LBL_component.Enable(True) 1608 self._PRW_component.Enable(True) 1609 self._PRW_substance.SetText(u'', None) 1610 else: 1611 self._LBL_or.Enable(False) 1612 self._LBL_component.Enable(False) 1613 self._PRW_component.SetText(u'', None) 1614 self._PRW_component.display_as_valid(True) 1615 self._PRW_component.Enable(False) 1616 self._LBL_preparation.Enable(True) 1617 self._PRW_preparation.Enable(True) 1618 self._TCTRL_brand_ingredients.SetValue(u'') 1619 self._TCTRL_brand_ingredients.SetToolTipString(u'')
1620 #----------------------------------------------------------------
1621 - def _on_discontinued_date_changed(self, event):
1622 if self._DP_discontinued.GetData() is None: 1623 self._PRW_discontinue_reason.Enable(False) 1624 else: 1625 self._PRW_discontinue_reason.Enable(True)
1626 #----------------------------------------------------------------
1627 - def _on_manage_brands_button_pressed(self, event):
1628 manage_branded_drugs(parent = self, ignore_OK_button = True)
1629 #----------------------------------------------------------------
1630 - def _on_manage_substances_button_pressed(self, event):
1631 manage_consumable_substances(parent = self)
1632 #----------------------------------------------------------------
1633 - def _on_heart_button_pressed(self, event):
1634 gmNetworkTools.open_url_in_browser(url = u'http://qtdrugs.org')
1635 #----------------------------------------------------------------
1636 - def _on_kidneys_button_pressed(self, event):
1637 if self._PRW_component.GetData() is not None: 1638 search_term = self._PRW_component.GetData(as_instance = True) 1639 elif self._PRW_substance.GetData() is not None: 1640 search_term = self._PRW_substance.GetData(as_instance = True) 1641 elif self._PRW_component.GetValue().strip() != u'': 1642 search_term = self._PRW_component.GetValue().strip() 1643 else: 1644 search_term = self._PRW_substance.GetValue().strip() 1645 1646 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
1647 #----------------------------------------------------------------
1649 1650 now = gmDateTime.pydt_now_here() 1651 1652 self.__refresh_allergies() 1653 1654 if self.data is None: 1655 return 1656 1657 # do we have a (full) plan ? 1658 if None not in [self.data['started'], self.data['duration']]: 1659 planned_end = self.data['started'] + self.data['duration'] 1660 # the plan hasn't ended so [Per plan] can't apply ;-) 1661 if planned_end > now: 1662 return 1663 self._DP_discontinued.SetData(planned_end) 1664 self._PRW_discontinue_reason.Enable(True) 1665 self._PRW_discontinue_reason.SetValue(u'') 1666 return 1667 1668 # we know started but not duration: apparently the plan is to stop today 1669 if self.data['started'] is not None: 1670 # but we haven't started yet so we can't stop 1671 if self.data['started'] > now: 1672 return 1673 1674 self._DP_discontinued.SetData(now) 1675 self._PRW_discontinue_reason.Enable(True) 1676 self._PRW_discontinue_reason.SetValue(u'')
1677 #----------------------------------------------------------------
1678 - def _on_chbox_long_term_checked(self, event):
1679 if self._CHBOX_long_term.IsChecked() is True: 1680 self._PRW_duration.Enable(False) 1681 self._BTN_discontinued_as_planned.Enable(False) 1682 self._PRW_discontinue_reason.Enable(False) 1683 else: 1684 self._PRW_duration.Enable(True) 1685 self._BTN_discontinued_as_planned.Enable(True) 1686 self._PRW_discontinue_reason.Enable(True) 1687 1688 self.__refresh_allergies()
1689 #----------------------------------------------------------------
1690 - def turn_into_allergy(self, data=None):
1691 if not self.save(): 1692 return False 1693 1694 return turn_substance_intake_into_allergy ( 1695 parent = self, 1696 intake = self.data, 1697 emr = gmPerson.gmCurrentPatient().get_emr() 1698 )
1699 #============================================================
1700 -def delete_substance_intake(parent=None, substance=None):
1701 1702 subst = gmMedication.cSubstanceIntakeEntry(aPK_obj = substance) 1703 msg = _( 1704 '\n' 1705 '[%s]\n' 1706 '\n' 1707 'It may be prudent to edit (before deletion) the details\n' 1708 'of this substance intake entry so as to leave behind\n' 1709 'some indication of why it was deleted.\n' 1710 ) % subst.format() 1711 1712 dlg = gmGuiHelpers.c3ButtonQuestionDlg ( 1713 parent, 1714 -1, 1715 caption = _('Deleting medication / substance intake'), 1716 question = msg, 1717 button_defs = [ 1718 {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True}, 1719 {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')}, 1720 {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')} 1721 ] 1722 ) 1723 1724 edit_first = dlg.ShowModal() 1725 dlg.Destroy() 1726 1727 if edit_first == wx.ID_CANCEL: 1728 return 1729 1730 if edit_first == wx.ID_YES: 1731 edit_intake_of_substance(parent = parent, substance = subst) 1732 delete_it = gmGuiHelpers.gm_show_question ( 1733 aMessage = _('Now delete substance intake entry ?'), 1734 aTitle = _('Deleting medication / substance intake') 1735 ) 1736 else: 1737 delete_it = True 1738 1739 if not delete_it: 1740 return 1741 1742 gmMedication.delete_substance_intake(substance = substance)
1743 #------------------------------------------------------------
1744 -def edit_intake_of_substance(parent = None, substance=None):
1745 ea = cSubstanceIntakeEAPnl(parent = parent, id = -1, substance = substance) 1746 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (substance is not None)) 1747 dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake'))) 1748 dlg.left_extra_button = ( 1749 _('Allergy'), 1750 _('Document an allergy against this substance.'), 1751 ea.turn_into_allergy 1752 ) 1753 if dlg.ShowModal() == wx.ID_OK: 1754 dlg.Destroy() 1755 return True 1756 dlg.Destroy() 1757 return False
1758 1759 #============================================================ 1760 # current substances grid 1761 #------------------------------------------------------------
1762 -def configure_medication_list_template(parent=None):
1763 1764 if parent is None: 1765 parent = wx.GetApp().GetTopWindow() 1766 1767 template = gmFormWidgets.manage_form_templates ( 1768 parent = parent, 1769 template_types = ['current medication list'] 1770 ) 1771 option = u'form_templates.medication_list' 1772 1773 if template is None: 1774 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 1775 return None 1776 1777 if template['engine'] not in [u'L', u'X']: 1778 gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True) 1779 return None 1780 1781 dbcfg = gmCfg.cCfgSQL() 1782 dbcfg.set ( 1783 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1784 option = option, 1785 value = u'%s - %s' % (template['name_long'], template['external_version']) 1786 ) 1787 1788 return template
1789 #------------------------------------------------------------ 1869 #------------------------------------------------------------
1870 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
1871 1872 if len(prescribed_drugs) == 0: 1873 return 1874 1875 curr_brands = [ i['pk_brand'] for i in emr.get_current_substance_intake() if i['pk_brand'] is not None ] 1876 new_drugs = [] 1877 for drug in prescribed_drugs: 1878 if drug['pk_brand'] not in curr_brands: 1879 new_drugs.append(drug) 1880 1881 if len(new_drugs) == 0: 1882 return 1883 1884 if parent is None: 1885 parent = wx.GetApp().GetTopWindow() 1886 1887 dlg = gmListWidgets.cItemPickerDlg ( 1888 parent, 1889 -1, 1890 msg = _( 1891 'These brands have been prescribed but are not listed\n' 1892 'in the current medication list of this patient.\n' 1893 '\n' 1894 'Please select those you want added to the medication list.' 1895 ) 1896 ) 1897 dlg.set_columns ( 1898 columns = [_('Newly prescribed drugs')], 1899 columns_right = [_('Add to medication list')] 1900 ) 1901 choices = [ (u'%s %s (%s)' % (d['brand'], d['preparation'], u'; '.join(d['components']))) for d in new_drugs ] 1902 dlg.set_choices ( 1903 choices = choices, 1904 data = new_drugs 1905 ) 1906 dlg.ShowModal() 1907 drugs2add = dlg.get_picks() 1908 dlg.Destroy() 1909 1910 if drugs2add is None: 1911 return 1912 1913 if len(drugs2add) == 0: 1914 return 1915 1916 for drug in drugs2add: 1917 # only add first component since all other components get added by a trigger ... 1918 intake = emr.add_substance_intake ( 1919 pk_component = drug['pk_components'][0], 1920 episode = emr.add_episode(episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE)['pk_episode'], 1921 ) 1922 if intake is None: 1923 continue 1924 intake['intake_is_approved_of'] = True 1925 intake.save() 1926 1927 return
1928 #------------------------------------------------------------
1929 -class cCurrentSubstancesGrid(wx.grid.Grid):
1930 """A grid class for displaying current substance intake. 1931 1932 - does NOT listen to the currently active patient 1933 - thereby it can display any patient at any time 1934 """
1935 - def __init__(self, *args, **kwargs):
1936 1937 wx.grid.Grid.__init__(self, *args, **kwargs) 1938 1939 self.__patient = None 1940 self.__row_data = {} 1941 self.__prev_row = None 1942 self.__prev_tooltip_row = None 1943 self.__prev_cell_0 = None 1944 self.__grouping_mode = u'issue' 1945 self.__filter_show_unapproved = True 1946 self.__filter_show_inactive = True 1947 1948 self.__grouping2col_labels = { 1949 u'issue': [ 1950 _('Health issue'), 1951 _('Substance'), 1952 _('Strength'), 1953 _('Schedule'), 1954 _('Started'), 1955 _('Duration / Until'), 1956 _('Brand'), 1957 _('Advice') 1958 ], 1959 u'brand': [ 1960 _('Brand'), 1961 _('Schedule'), 1962 _('Substance'), 1963 _('Strength'), 1964 _('Started'), 1965 _('Duration / Until'), 1966 _('Health issue'), 1967 _('Advice') 1968 ], 1969 u'episode': [ 1970 _('Episode'), 1971 _('Substance'), 1972 _('Strength'), 1973 _('Schedule'), 1974 _('Started'), 1975 _('Duration / Until'), 1976 _('Brand'), 1977 _('Advice') 1978 ] 1979 } 1980 1981 self.__grouping2order_by_clauses = { 1982 u'issue': u'pk_health_issue nulls first, substance, started', 1983 u'episode': u'pk_health_issue nulls first, episode, substance, started', 1984 u'brand': u'brand nulls last, substance, started' 1985 } 1986 1987 self.__init_ui() 1988 self.__register_events()
1989 #------------------------------------------------------------ 1990 # external API 1991 #------------------------------------------------------------
1992 - def get_selected_cells(self):
1993 1994 sel_block_top_left = self.GetSelectionBlockTopLeft() 1995 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 1996 sel_cols = self.GetSelectedCols() 1997 sel_rows = self.GetSelectedRows() 1998 1999 selected_cells = [] 2000 2001 # individually selected cells (ctrl-click) 2002 selected_cells += self.GetSelectedCells() 2003 2004 # selected rows 2005 selected_cells += list ( 2006 (row, col) 2007 for row in sel_rows 2008 for col in xrange(self.GetNumberCols()) 2009 ) 2010 2011 # selected columns 2012 selected_cells += list ( 2013 (row, col) 2014 for row in xrange(self.GetNumberRows()) 2015 for col in sel_cols 2016 ) 2017 2018 # selection blocks 2019 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 2020 selected_cells += [ 2021 (row, col) 2022 for row in xrange(top_left[0], bottom_right[0] + 1) 2023 for col in xrange(top_left[1], bottom_right[1] + 1) 2024 ] 2025 2026 return set(selected_cells)
2027 #------------------------------------------------------------
2028 - def get_selected_rows(self):
2029 rows = {} 2030 2031 for row, col in self.get_selected_cells(): 2032 rows[row] = True 2033 2034 return rows.keys()
2035 #------------------------------------------------------------
2036 - def get_selected_data(self):
2037 return [ self.__row_data[row] for row in self.get_selected_rows() ]
2038 #------------------------------------------------------------
2039 - def repopulate_grid(self):
2040 2041 self.empty_grid() 2042 2043 if self.__patient is None: 2044 return 2045 2046 emr = self.__patient.get_emr() 2047 meds = emr.get_current_substance_intake ( 2048 order_by = self.__grouping2order_by_clauses[self.__grouping_mode], 2049 include_unapproved = self.__filter_show_unapproved, 2050 include_inactive = self.__filter_show_inactive 2051 ) 2052 if not meds: 2053 return 2054 2055 self.BeginBatch() 2056 2057 # columns 2058 labels = self.__grouping2col_labels[self.__grouping_mode] 2059 if self.__filter_show_unapproved: 2060 self.AppendCols(numCols = len(labels) + 1) 2061 else: 2062 self.AppendCols(numCols = len(labels)) 2063 for col_idx in range(len(labels)): 2064 self.SetColLabelValue(col_idx, labels[col_idx]) 2065 if self.__filter_show_unapproved: 2066 self.SetColLabelValue(len(labels), u'OK?') 2067 self.SetColSize(len(labels), 40) 2068 2069 self.AppendRows(numRows = len(meds)) 2070 2071 # loop over data 2072 for row_idx in range(len(meds)): 2073 med = meds[row_idx] 2074 self.__row_data[row_idx] = med 2075 2076 if med['is_currently_active'] is True: 2077 atcs = [] 2078 if med['atc_substance'] is not None: 2079 atcs.append(med['atc_substance']) 2080 # if med['atc_brand'] is not None: 2081 # atcs.append(med['atc_brand']) 2082 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],), brand = med['brand']) 2083 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],)) 2084 if allg not in [None, False]: 2085 attr = self.GetOrCreateCellAttr(row_idx, 0) 2086 if allg['type'] == u'allergy': 2087 attr.SetTextColour('red') 2088 else: 2089 attr.SetTextColour('yellow') 2090 self.SetRowAttr(row_idx, attr) 2091 else: 2092 attr = self.GetOrCreateCellAttr(row_idx, 0) 2093 attr.SetTextColour('grey') 2094 self.SetRowAttr(row_idx, attr) 2095 2096 if self.__grouping_mode == u'episode': 2097 if med['pk_episode'] is None: 2098 self.__prev_cell_0 = None 2099 epi = gmTools.u_diameter 2100 else: 2101 if self.__prev_cell_0 == med['episode']: 2102 epi = u'' 2103 else: 2104 self.__prev_cell_0 = med['episode'] 2105 epi = gmTools.coalesce(med['episode'], u'') 2106 self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40)) 2107 2108 self.SetCellValue(row_idx, 1, med['substance']) 2109 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit'])) 2110 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u'')) 2111 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 2112 2113 if med['is_long_term']: 2114 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 2115 else: 2116 if med['discontinued'] is None: 2117 if med['duration'] is None: 2118 self.SetCellValue(row_idx, 5, u'') 2119 else: 2120 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 2121 else: 2122 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d')) 2123 2124 if med['pk_brand'] is None: 2125 brand = u'' 2126 else: 2127 if med['fake_brand']: 2128 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)')) 2129 else: 2130 brand = gmTools.coalesce(med['brand'], u'') 2131 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35)) 2132 2133 elif self.__grouping_mode == u'issue': 2134 if med['pk_health_issue'] is None: 2135 self.__prev_cell_0 = None 2136 issue = u'%s%s' % ( 2137 gmTools.u_diameter, 2138 gmTools.coalesce(med['episode'], u'', u' (%s)') 2139 ) 2140 else: 2141 if self.__prev_cell_0 == med['health_issue']: 2142 issue = u'' 2143 else: 2144 self.__prev_cell_0 = med['health_issue'] 2145 issue = med['health_issue'] 2146 self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40)) 2147 2148 self.SetCellValue(row_idx, 1, med['substance']) 2149 self.SetCellValue(row_idx, 2, u'%s %s' % (med['amount'], med['unit'])) 2150 self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], u'')) 2151 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 2152 2153 if med['is_long_term']: 2154 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 2155 else: 2156 if med['discontinued'] is None: 2157 if med['duration'] is None: 2158 self.SetCellValue(row_idx, 5, u'') 2159 else: 2160 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 2161 else: 2162 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d')) 2163 2164 if med['pk_brand'] is None: 2165 brand = u'' 2166 else: 2167 if med['fake_brand']: 2168 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)')) 2169 else: 2170 brand = gmTools.coalesce(med['brand'], u'') 2171 self.SetCellValue(row_idx, 6, gmTools.wrap(text = brand, width = 35)) 2172 2173 elif self.__grouping_mode == u'brand': 2174 2175 if med['pk_brand'] is None: 2176 self.__prev_cell_0 = None 2177 brand = gmTools.u_diameter 2178 else: 2179 if self.__prev_cell_0 == med['brand']: 2180 brand = u'' 2181 else: 2182 self.__prev_cell_0 = med['brand'] 2183 if med['fake_brand']: 2184 brand = gmTools.coalesce(med['brand'], u'', _('%s (fake)')) 2185 else: 2186 brand = gmTools.coalesce(med['brand'], u'') 2187 self.SetCellValue(row_idx, 0, gmTools.wrap(text = brand, width = 35)) 2188 2189 self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], u'')) 2190 self.SetCellValue(row_idx, 2, med['substance']) 2191 self.SetCellValue(row_idx, 3, u'%s %s' % (med['amount'], med['unit'])) 2192 self.SetCellValue(row_idx, 4, med['started'].strftime('%Y-%m-%d')) 2193 2194 if med['is_long_term']: 2195 self.SetCellValue(row_idx, 5, gmTools.u_infinity) 2196 else: 2197 if med['discontinued'] is None: 2198 if med['duration'] is None: 2199 self.SetCellValue(row_idx, 5, u'') 2200 else: 2201 self.SetCellValue(row_idx, 5, gmDateTime.format_interval(med['duration'], gmDateTime.acc_days)) 2202 else: 2203 self.SetCellValue(row_idx, 5, med['discontinued'].strftime('%Y-%m-%d')) 2204 2205 if med['pk_health_issue'] is None: 2206 issue = u'%s%s' % ( 2207 gmTools.u_diameter, 2208 gmTools.coalesce(med['episode'], u'', u' (%s)') 2209 ) 2210 else: 2211 issue = gmTools.coalesce(med['health_issue'], u'') 2212 self.SetCellValue(row_idx, 6, gmTools.wrap(text = issue, width = 40)) 2213 2214 else: 2215 raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode) 2216 2217 if med['notes'] is not None: 2218 self.SetCellValue(row_idx, 7, gmTools.wrap(text = med['notes'], width = 50)) 2219 2220 if self.__filter_show_unapproved: 2221 self.SetCellValue ( 2222 row_idx, 2223 len(labels), 2224 gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, u'', u'?') 2225 ) 2226 2227 #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 2228 2229 self.AutoSize() 2230 self.EndBatch()
2231 #------------------------------------------------------------
2232 - def empty_grid(self):
2233 self.BeginBatch() 2234 self.ClearGrid() 2235 # Windows cannot do "nothing", it rather decides to assert() 2236 # on thinking it is supposed to do nothing 2237 if self.GetNumberRows() > 0: 2238 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 2239 if self.GetNumberCols() > 0: 2240 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 2241 self.EndBatch() 2242 self.__row_data = {} 2243 self.__prev_cell_0 = None
2244 #------------------------------------------------------------
2245 - def show_info_on_entry(self):
2246 2247 if len(self.__row_data) == 0: 2248 return 2249 2250 sel_rows = self.get_selected_rows() 2251 if len(sel_rows) != 1: 2252 return 2253 2254 drug_db = get_drug_database() 2255 if drug_db is None: 2256 return 2257 2258 intake = self.get_selected_data()[0] # just in case 2259 if intake['brand'] is None: 2260 drug_db.show_info_on_substance(substance_intake = intake) 2261 else: 2262 drug_db.show_info_on_drug(substance_intake = intake)
2263 #------------------------------------------------------------
2265 search_term = None 2266 if len(self.__row_data) > 0: 2267 sel_rows = self.get_selected_rows() 2268 if len(sel_rows) == 1: 2269 search_term = self.get_selected_data()[0] 2270 gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
2271 #------------------------------------------------------------
2272 - def show_cardiac_info(self):
2273 gmNetworkTools.open_url_in_browser(url = u'http://qtdrugs.org')
2274 #------------------------------------------------------------
2275 - def report_ADR(self):
2276 2277 dbcfg = gmCfg.cCfgSQL() 2278 2279 url = dbcfg.get2 ( 2280 option = u'external.urls.report_ADR', 2281 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2282 bias = u'user', 2283 default = u'https://dcgma.org/uaw/meldung.php' # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html 2284 ) 2285 gmNetworkTools.open_url_in_browser(url = url)
2286 #------------------------------------------------------------
2287 - def prescribe(self):
2288 drug_db = get_drug_database() 2289 if drug_db is None: 2290 return 2291 2292 drug_db.reviewer = gmStaff.gmCurrentProvider() 2293 update_substance_intake_list_from_prescription ( 2294 parent = self, 2295 prescribed_drugs = drug_db.prescribe(), 2296 emr = self.__patient.get_emr() 2297 )
2298 #------------------------------------------------------------
2299 - def check_interactions(self):
2300 2301 if len(self.__row_data) == 0: 2302 return 2303 2304 drug_db = get_drug_database() 2305 if drug_db is None: 2306 return 2307 2308 if len(self.get_selected_rows()) > 1: 2309 drug_db.check_interactions(substance_intakes = self.get_selected_data()) 2310 else: 2311 drug_db.check_interactions(substance_intakes = self.__row_data.values())
2312 #------------------------------------------------------------
2313 - def add_substance(self):
2314 edit_intake_of_substance(parent = self, substance = None)
2315 #------------------------------------------------------------
2316 - def edit_substance(self):
2317 2318 rows = self.get_selected_rows() 2319 2320 if len(rows) == 0: 2321 return 2322 2323 if len(rows) > 1: 2324 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True) 2325 return 2326 2327 subst = self.get_selected_data()[0] 2328 edit_intake_of_substance(parent = self, substance = subst)
2329 #------------------------------------------------------------
2330 - def delete_substance(self):
2331 2332 rows = self.get_selected_rows() 2333 2334 if len(rows) == 0: 2335 return 2336 2337 if len(rows) > 1: 2338 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True) 2339 return 2340 2341 subst = self.get_selected_data()[0] 2342 delete_substance_intake(parent = self, substance = subst['pk_substance_intake'])
2343 #------------------------------------------------------------
2345 rows = self.get_selected_rows() 2346 2347 if len(rows) == 0: 2348 return 2349 2350 if len(rows) > 1: 2351 gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True) 2352 return 2353 2354 return turn_substance_intake_into_allergy ( 2355 parent = self, 2356 intake = self.get_selected_data()[0], 2357 emr = self.__patient.get_emr() 2358 )
2359 #------------------------------------------------------------
2360 - def print_medication_list(self):
2361 # there could be some filtering/user interaction going on here 2362 print_medication_list(parent = self)
2363 #------------------------------------------------------------
2364 - def get_row_tooltip(self, row=None):
2365 2366 try: 2367 entry = self.__row_data[row] 2368 except KeyError: 2369 return u' ' 2370 2371 emr = self.__patient.get_emr() 2372 atcs = [] 2373 if entry['atc_substance'] is not None: 2374 atcs.append(entry['atc_substance']) 2375 # if entry['atc_brand'] is not None: 2376 # atcs.append(entry['atc_brand']) 2377 # allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), brand = entry['brand']) 2378 allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],)) 2379 2380 tt = _('Substance intake entry (%s, %s) [#%s] \n') % ( 2381 gmTools.bool2subst ( 2382 boolean = entry['is_currently_active'], 2383 true_return = gmTools.bool2subst ( 2384 boolean = entry['seems_inactive'], 2385 true_return = _('active, needs check'), 2386 false_return = _('active'), 2387 none_return = _('assumed active') 2388 ), 2389 false_return = _('inactive') 2390 ), 2391 gmTools.bool2subst ( 2392 boolean = entry['intake_is_approved_of'], 2393 true_return = _('approved'), 2394 false_return = _('unapproved') 2395 ), 2396 entry['pk_substance_intake'] 2397 ) 2398 2399 if allg not in [None, False]: 2400 certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected')) 2401 tt += u'\n' 2402 tt += u' !! ---- Cave ---- !!\n' 2403 tt += u' %s (%s): %s (%s)\n' % ( 2404 allg['l10n_type'], 2405 certainty, 2406 allg['descriptor'], 2407 gmTools.coalesce(allg['reaction'], u'')[:40] 2408 ) 2409 tt += u'\n' 2410 2411 tt += u' ' + _('Substance: %s [#%s]\n') % (entry['substance'], entry['pk_substance']) 2412 tt += u' ' + _('Preparation: %s\n') % entry['preparation'] 2413 tt += u' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit']) 2414 if entry.ddd is not None: 2415 tt += u' (DDD: %s %s)' % (entry.ddd['ddd'], entry.ddd['unit']) 2416 tt += u'\n' 2417 tt += gmTools.coalesce(entry['atc_substance'], u'', _(' ATC (substance): %s\n')) 2418 2419 tt += u'\n' 2420 2421 tt += gmTools.coalesce ( 2422 entry['brand'], 2423 u'', 2424 _(' Brand name: %%s [#%s]\n') % entry['pk_brand'] 2425 ) 2426 tt += gmTools.coalesce(entry['atc_brand'], u'', _(' ATC (brand): %s\n')) 2427 2428 tt += u'\n' 2429 2430 tt += gmTools.coalesce(entry['schedule'], u'', _(' Regimen: %s\n')) 2431 2432 if entry['is_long_term']: 2433 duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity) 2434 else: 2435 if entry['duration'] is None: 2436 duration = u'' 2437 else: 2438 duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days)) 2439 2440 tt += _(' Started %s%s%s\n') % ( 2441 entry['started'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 2442 duration, 2443 gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), u'') 2444 ) 2445 2446 if entry['discontinued'] is not None: 2447 tt += _(' Discontinued %s\n') % ( 2448 entry['discontinued'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 2449 ) 2450 tt += _(' Reason: %s\n') % entry['discontinue_reason'] 2451 2452 tt += u'\n' 2453 2454 tt += gmTools.coalesce(entry['aim'], u'', _(' Aim: %s\n')) 2455 tt += gmTools.coalesce(entry['episode'], u'', _(' Episode: %s\n')) 2456 tt += gmTools.coalesce(entry['health_issue'], u'', _(' Health issue: %s\n')) 2457 tt += gmTools.coalesce(entry['notes'], u'', _(' Advice: %s\n')) 2458 2459 tt += u'\n' 2460 2461 tt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({ 2462 'row_ver': entry['row_version'], 2463 'mod_when': entry['modified_when'].strftime('%c').decode(gmI18N.get_encoding()), 2464 'mod_by': entry['modified_by'] 2465 }) 2466 2467 return tt
2468 #------------------------------------------------------------ 2469 # internal helpers 2470 #------------------------------------------------------------
2471 - def __init_ui(self):
2472 self.CreateGrid(0, 1) 2473 self.EnableEditing(0) 2474 self.EnableDragGridSize(1) 2475 self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows) 2476 2477 self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER) 2478 2479 self.SetRowLabelSize(0) 2480 self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
2481 #------------------------------------------------------------ 2482 # properties 2483 #------------------------------------------------------------
2484 - def _get_patient(self):
2485 return self.__patient
2486
2487 - def _set_patient(self, patient):
2488 self.__patient = patient 2489 self.repopulate_grid()
2490 2491 patient = property(_get_patient, _set_patient) 2492 #------------------------------------------------------------
2493 - def _get_grouping_mode(self):
2494 return self.__grouping_mode
2495
2496 - def _set_grouping_mode(self, mode):
2497 self.__grouping_mode = mode 2498 self.repopulate_grid()
2499 2500 grouping_mode = property(_get_grouping_mode, _set_grouping_mode) 2501 #------------------------------------------------------------
2503 return self.__filter_show_unapproved
2504
2505 - def _set_filter_show_unapproved(self, val):
2506 self.__filter_show_unapproved = val 2507 self.repopulate_grid()
2508 2509 filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved) 2510 #------------------------------------------------------------
2511 - def _get_filter_show_inactive(self):
2512 return self.__filter_show_inactive
2513
2514 - def _set_filter_show_inactive(self, val):
2515 self.__filter_show_inactive = val 2516 self.repopulate_grid()
2517 2518 filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive) 2519 #------------------------------------------------------------ 2520 # event handling 2521 #------------------------------------------------------------
2522 - def __register_events(self):
2523 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 2524 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 2525 #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 2526 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 2527 2528 # editing cells 2529 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2530 #------------------------------------------------------------
2531 - def __on_mouse_over_cells(self, evt):
2532 """Calculate where the mouse is and set the tooltip dynamically.""" 2533 2534 # Use CalcUnscrolledPosition() to get the mouse position within the 2535 # entire grid including what's offscreen 2536 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 2537 2538 # use this logic to prevent tooltips outside the actual cells 2539 # apply to GetRowSize, too 2540 # tot = 0 2541 # for col in xrange(self.NumberCols): 2542 # tot += self.GetColSize(col) 2543 # if xpos <= tot: 2544 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 2545 # self.GetColLabelValue(col)) 2546 # break 2547 # else: # mouse is in label area beyond the right-most column 2548 # self.tool_tip.Tip = '' 2549 2550 row, col = self.XYToCell(x, y) 2551 2552 if row == self.__prev_tooltip_row: 2553 return 2554 2555 self.__prev_tooltip_row = row 2556 2557 try: 2558 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row)) 2559 except KeyError: 2560 pass
2561 #------------------------------------------------------------
2562 - def __on_cell_left_dclicked(self, evt):
2563 row = evt.GetRow() 2564 data = self.__row_data[row] 2565 edit_intake_of_substance(parent = self, substance = data)
2566 #============================================================ 2567 from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl 2568
2569 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
2570 2571 """Panel holding a grid with current substances. Used as notebook page.""" 2572
2573 - def __init__(self, *args, **kwargs):
2574 2575 wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs) 2576 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 2577 2578 self.__register_interests()
2579 #----------------------------------------------------- 2580 # reget-on-paint mixin API 2581 #-----------------------------------------------------
2582 - def _populate_with_data(self):
2583 """Populate cells with data from model.""" 2584 pat = gmPerson.gmCurrentPatient() 2585 if pat.connected: 2586 self._grid_substances.patient = pat 2587 else: 2588 self._grid_substances.patient = None 2589 return True
2590 #-------------------------------------------------------- 2591 # event handling 2592 #--------------------------------------------------------
2593 - def __register_interests(self):
2594 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 2595 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget) 2596 gmDispatcher.connect(signal = u'substance_intake_mod_db', receiver = self._schedule_data_reget)
2597 # active_substance_mod_db 2598 # substance_brand_mod_db 2599 #--------------------------------------------------------
2600 - def _on_pre_patient_selection(self):
2601 wx.CallAfter(self.__on_pre_patient_selection)
2602 #--------------------------------------------------------
2603 - def __on_pre_patient_selection(self):
2604 self._grid_substances.patient = None
2605 #--------------------------------------------------------
2606 - def _on_add_button_pressed(self, event):
2607 self._grid_substances.add_substance()
2608 #--------------------------------------------------------
2609 - def _on_edit_button_pressed(self, event):
2610 self._grid_substances.edit_substance()
2611 #--------------------------------------------------------
2612 - def _on_delete_button_pressed(self, event):
2613 self._grid_substances.delete_substance()
2614 #--------------------------------------------------------
2615 - def _on_info_button_pressed(self, event):
2616 self._grid_substances.show_info_on_entry()
2617 #--------------------------------------------------------
2618 - def _on_interactions_button_pressed(self, event):
2619 self._grid_substances.check_interactions()
2620 #--------------------------------------------------------
2621 - def _on_issue_grouping_selected(self, event):
2622 self._grid_substances.grouping_mode = 'issue'
2623 #--------------------------------------------------------
2624 - def _on_episode_grouping_selected(self, event):
2625 self._grid_substances.grouping_mode = 'episode'
2626 #--------------------------------------------------------
2627 - def _on_brand_grouping_selected(self, event):
2628 self._grid_substances.grouping_mode = 'brand'
2629 #--------------------------------------------------------
2630 - def _on_show_unapproved_checked(self, event):
2631 self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
2632 #--------------------------------------------------------
2633 - def _on_show_inactive_checked(self, event):
2634 self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
2635 #--------------------------------------------------------
2636 - def _on_print_button_pressed(self, event):
2637 self._grid_substances.print_medication_list()
2638 #--------------------------------------------------------
2639 - def _on_allergy_button_pressed(self, event):
2640 self._grid_substances.create_allergy_from_substance()
2641 #--------------------------------------------------------
2642 - def _on_button_kidneys_pressed(self, event):
2643 self._grid_substances.show_renal_insufficiency_info()
2644 #--------------------------------------------------------
2645 - def _on_button_heart_pressed(self, event):
2646 self._grid_substances.show_cardiac_info()
2647 #--------------------------------------------------------
2648 - def _on_adr_button_pressed(self, event):
2649 self._grid_substances.report_ADR()
2650 #--------------------------------------------------------
2651 - def _on_rx_button_pressed(self, event):
2652 self._grid_substances.prescribe()
2653 #============================================================ 2654 # main 2655 #------------------------------------------------------------ 2656 if __name__ == '__main__': 2657 2658 if len(sys.argv) < 2: 2659 sys.exit() 2660 2661 if sys.argv[1] != 'test': 2662 sys.exit() 2663 2664 from Gnumed.pycommon import gmI18N 2665 2666 gmI18N.activate_locale() 2667 gmI18N.install_domain(domain = 'gnumed') 2668 2669 #---------------------------------------- 2670 app = wx.PyWidgetTester(size = (600, 600)) 2671 #app.SetWidget(cATCPhraseWheel, -1) 2672 app.SetWidget(cSubstancePhraseWheel, -1) 2673 app.MainLoop() 2674 2675 #============================================================ 2676