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

# Source Code for Module Gnumed.wxpython.gmMedicationWidgets

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