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