Package Gnumed :: Package wxpython :: Module gmMedicationWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmMedicationWidgets

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