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