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