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