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

Source Code for Module Gnumed.wxpython.gmMedicationWidgets

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