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