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