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

# Source Code for Module Gnumed.wxpython.gmSubstanceMgmtWidgets

       1  # -*- coding: utf-8 -*-
       2
       3  #from __future__ import print_function
       4
       5  __doc__ = """GNUmed drug / substance reference widgets."""
       6
       7  #================================================================
       8  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
       9  __license__ = "GPL v2 or later"
      10
      11  import logging
      12  import sys
      13  import os.path
      14  import decimal
      15
      16
      17  import wx
      18
      19
      20  if __name__ == '__main__':
      21          sys.path.insert(0, '../../')
      22          from Gnumed.pycommon import gmI18N
      23          gmI18N.activate_locale()
      24          gmI18N.install_domain(domain = 'gnumed')
      25
      26  from Gnumed.pycommon import gmDispatcher
      27  from Gnumed.pycommon import gmCfg
      28  from Gnumed.pycommon import gmShellAPI
      29  from Gnumed.pycommon import gmTools
      30  from Gnumed.pycommon import gmMatchProvider
      31
      32  from Gnumed.business import gmPerson
      33  from Gnumed.business import gmPraxis
      34  from Gnumed.business import gmMedication
      35  from Gnumed.business import gmDrugDataSources
      36  from Gnumed.business import gmATC
      37
      38  from Gnumed.wxpython import gmGuiHelpers
      39  from Gnumed.wxpython import gmEditArea
      40  from Gnumed.wxpython import gmCfgWidgets
      41  from Gnumed.wxpython import gmListWidgets
      42  from Gnumed.wxpython import gmPhraseWheel
      43
      44
      45  _log = logging.getLogger('gm.ui')
      46
      47  #============================================================
      48  # generic drug database access
      49  #------------------------------------------------------------
      50 -def configure_drug_data_source(parent=None):
      51          gmCfgWidgets.configure_string_from_list_option (
      52                  parent = parent,
      53                  message = _(
      54                          '\n'
      55                          'Please select the default drug data source from the list below.\n'
      56                          '\n'
      57                          'Note that to actually use it you need to have the database installed, too.'
      58                  ),
      59                  option = 'external.drug_data.default_source',
      60                  bias = 'user',
      61                  default_value = None,
      62                  choices = gmDrugDataSources.drug_data_source_interfaces.keys(),
      63                  columns = [_('Drug data source')],
      64                  data = gmDrugDataSources.drug_data_source_interfaces.keys(),
      65                  caption = _('Configuring default drug data source')
      66          )
      67
      68  #============================================================
      69 -def get_drug_database(parent=None, patient=None):
      70          dbcfg = gmCfg.cCfgSQL()
      71
      72          # load from option
      73          default_db = dbcfg.get2 (
      74                  option = 'external.drug_data.default_source',
      75                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
      76                  bias = 'workplace'
      77          )
      78
      79          # not configured -> try to configure
      80          if default_db is None:
      81                  gmDispatcher.send('statustext', msg = _('No default drug database configured.'), beep = True)
      82                  configure_drug_data_source(parent = parent)
      83                  default_db = dbcfg.get2 (
      84                          option = 'external.drug_data.default_source',
      85                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
      86                          bias = 'workplace'
      87                  )
      88                  # still not configured -> return
      89                  if default_db is None:
      90                          gmGuiHelpers.gm_show_error (
      91                                  aMessage = _('There is no default drug database configured.'),
      92                                  aTitle = _('Jumping to drug database')
      93                          )
      94                          return None
      95
      96          # now it MUST be configured (either newly or previously)
      97          # but also *validly* ?
      98          try:
      99                  drug_db = gmDrugDataSources.drug_data_source_interfaces[default_db]()
     100          except KeyError:
     101                  # not valid
     102                  _log.error('faulty default drug data source configuration: %s', default_db)
     103                  # try to configure
     104                  configure_drug_data_source(parent = parent)
     105                  default_db = dbcfg.get2 (
     106                          option = 'external.drug_data.default_source',
     107                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
     108                          bias = 'workplace'
     109                  )
     110                  # deconfigured or aborted (and thusly still misconfigured) ?
     111                  try:
     112                          drug_db = gmDrugDataSources.drug_data_source_interfaces[default_db]()
     113                  except KeyError:
     114                          _log.error('still faulty default drug data source configuration: %s', default_db)
     115                          return None
     116
     117          if patient is not None:
     118                  drug_db.patient = patient
     119
     120          return drug_db
     121
     122  #============================================================
     123 -def jump_to_drug_database(patient=None):
     124          drug_db = get_drug_database(patient = patient)
     125          if drug_db is None:
     126                  return
     127          drug_db.switch_to_frontend(blocking = False)
     128
     129  #============================================================
     130 -def jump_to_ifap_deprecated(import_drugs=False, emr=None):
     131
     132          if import_drugs and (emr is None):
     133                  gmDispatcher.send('statustext', msg = _('Cannot import drugs from IFAP into chart without chart.'))
     134                  return False
     135
     136          dbcfg = gmCfg.cCfgSQL()
     137
     138          ifap_cmd = dbcfg.get2 (
     139                  option = 'external.ifap-win.shell_command',
     140                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
     141                  bias = 'workplace',
     142                  default = 'wine "C:\Ifapwin\WIAMDB.EXE"'
     143          )
     144          found, binary = gmShellAPI.detect_external_binary(ifap_cmd)
     145          if not found:
     146                  gmDispatcher.send('statustext', msg = _('Cannot call IFAP via [%s].') % ifap_cmd)
     147                  return False
     148          ifap_cmd = binary
     149
     150          if import_drugs:
     151                  transfer_file = os.path.expanduser(dbcfg.get2 (
     152                          option = 'external.ifap-win.transfer_file',
     153                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
     154                          bias = 'workplace',
     155                          default = '~/.wine/drive_c/Ifapwin/ifap2gnumed.csv'
     156                  ))
     157                  # file must exist for Ifap to write into it
     158                  try:
     159                          f = io.open(transfer_file, mode = 'wt').close()
     160                  except IOError:
     161                          _log.exception('Cannot create IFAP <-> GNUmed transfer file [%s]', transfer_file)
     162                          gmDispatcher.send('statustext', msg = _('Cannot create IFAP <-> GNUmed transfer file [%s].') % transfer_file)
     163                          return False
     164
     165          wx.BeginBusyCursor()
     166          gmShellAPI.run_command_in_shell(command = ifap_cmd, blocking = import_drugs)
     167          wx.EndBusyCursor()
     168
     169          if import_drugs:
     170                  # COMMENT: this file must exist PRIOR to invoking IFAP
     171                  # COMMENT: or else IFAP will not write data into it ...
     172                  try:
     173                          csv_file = io.open(transfer_file, mode = 'rt', encoding = 'latin1')                                             # FIXME: encoding unknown
     174                  except:
     175                          _log.exception('cannot access [%s]', fname)
     176                          csv_file = None
     177
     178                  if csv_file is not None:
     179                          import csv
     180                          csv_lines = csv.DictReader (
     181                                  csv_file,
     182                                  fieldnames = 'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split(),
     183                                  delimiter = ';'
     184                          )
     185                          # dummy episode for now
     186                          epi = emr.add_episode(episode_name = _('Current medication'))
     187                          for line in csv_lines:
     188                                  narr = '%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
     189                                          line['Packungszahl'].strip(),
     190                                          line['Handelsname'].strip(),
     191                                          line['Form'].strip(),
     192                                          line['Packungsgr\xf6\xdfe'].strip(),
     193                                          line['Abpackungsmenge'].strip(),
     194                                          line['Einheit'].strip(),
     195                                          line['Hersteller'].strip(),
     196                                          line['PZN'].strip()
     197                                  )
     198                                  emr.add_clin_narrative(note = narr, soap_cat = 's', episode = epi)
     199                          csv_file.close()
     200
     201          return True
     202
     203  #============================================================
     204  # substances widgets
     205  #------------------------------------------------------------
     206 -def edit_substance(parent=None, substance=None, single_entry=False):
     207
     208          if substance is not None:
     209                  if substance.is_in_use_by_patients:
     210                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
     211                          return False
     212
     213          ea = cSubstanceEAPnl(parent, -1)
     214          ea.data = substance
     215          ea.mode = gmTools.coalesce(substance, 'new', 'edit')
     216          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     217          dlg.SetTitle(gmTools.coalesce(substance, _('Adding new substance'), _('Editing substance')))
     218          if dlg.ShowModal() == wx.ID_OK:
     219                  dlg.DestroyLater()
     220                  return True
     221          dlg.DestroyLater()
     222          return False
     223
     224  #------------------------------------------------------------
     225 -def manage_substances(parent=None):
     226
     227          if parent is None:
     228                  parent = wx.GetApp().GetTopWindow()
     229
     230          #------------------------------------------------------------
     231          def add_from_db(substance):
     232                  drug_db = get_drug_database(parent = parent)
     233                  if drug_db is None:
     234                          return False
     235                  drug_db.import_drugs()
     236                  return True
     237
     238          #------------------------------------------------------------
     239          def edit(substance=None):
     240                  return edit_substance(parent = parent, substance = substance, single_entry = (substance is not None))
     241
     242          #------------------------------------------------------------
     243          def delete(substance):
     244                  if substance.is_in_use_by_patients:
     245                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
     246                          return False
     247                  return gmMedication.delete_substance(pk_substance = substance['pk_substance'])
     248
     249          #------------------------------------------------------------
     250          def get_item_tooltip(substance):
     251                  if not isinstance(substance, gmMedication.cSubstance):
     252                          return None
     253                  return substance.format()
     254
     255          #------------------------------------------------------------
     256          def refresh(lctrl):
     257                  substs = gmMedication.get_substances(order_by = 'substance')
     258                  items = [ [
     259                          s['substance'],
     260                          gmTools.coalesce(s['atc'], ''),
     261                          gmTools.coalesce(s['intake_instructions'], ''),
     262                          s['pk_substance']
     263                  ] for s in substs ]
     264                  lctrl.set_string_items(items)
     265                  lctrl.set_data(substs)
     266
     267          #------------------------------------------------------------
     268          gmListWidgets.get_choices_from_list (
     269                  parent = parent,
     270                  caption = _('Substances registered with GNUmed.'),
     271                  columns = [_('Substance'), 'ATC', _('Instructions'), '#'],
     272                  single_selection = True,
     273                  new_callback = edit,
     274                  edit_callback = edit,
     275                  delete_callback = delete,
     276                  refresh_callback = refresh,
     277                  left_extra_button = (_('Import'), _('Import substances from a drug database.'), add_from_db),
     278                  list_tooltip_callback = get_item_tooltip
     279          )
     280
     281  #------------------------------------------------------------
     282  from Gnumed.wxGladeWidgets import wxgSubstanceEAPnl
     283
     284 -class cSubstanceEAPnl(wxgSubstanceEAPnl.wxgSubstanceEAPnl, gmEditArea.cGenericEditAreaMixin):
     285
     286 -        def __init__(self, *args, **kwargs):
     287
     288                  try:
     289                          data = kwargs['substance']
     290                          del kwargs['substance']
     291                  except KeyError:
     292                          data = None
     293
     294                  wxgSubstanceEAPnl.wxgSubstanceEAPnl.__init__(self, *args, **kwargs)
     295                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     296
     297                  # Code using this mixin should set mode and data
     298                  # after instantiating the class:
     299                  self.mode = 'new'
     300                  self.data = data
     301                  if data is not None:
     302                          self.mode = 'edit'
     303
     304                  self.__init_ui()
     305
     306          #----------------------------------------------------------------
     307 -        def __init_ui(self):
     308                  self._LCTRL_loincs.set_columns([_('LOINC'), _('Interval'), _('Comment')])
     309
     310          #----------------------------------------------------------------
     311          # generic Edit Area mixin API
     312          #----------------------------------------------------------------
     313 -        def _valid_for_save(self):
     314
     315                  validity = True
     316
     317                  if self._TCTRL_substance.GetValue().strip() == '':
     318                          validity = False
     319                          self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = False)
     320                          self._TCTRL_substance.SetFocus()
     321                  else:
     322                          self.display_tctrl_as_valid(tctrl = self._TCTRL_substance, valid = True)
     323
     324                  if validity is False:
     325                          self.StatusText = _('Cannot save: Substance name missing.')
     326
     327                  return validity
     328
     329          #----------------------------------------------------------------
     330 -        def _save_as_new(self):
     331                  subst = gmMedication.create_substance (
     332                          substance = self._TCTRL_substance.GetValue().strip(),
     333                          atc = self._PRW_atc.GetData()
     334                  )
     335                  subst['intake_instructions'] = self._TCTRL_instructions.GetValue().strip()
     336                  success, data = subst.save()
     337                  if not success:
     338                          err, msg = data
     339                          _log.error(err)
     340                          _log.error(msg)
     341                          self.StatusText = _('Error saving substance. %s') % msg
     342                          return False
     343
     344                  loincs = self._LCTRL_loincs.item_data
     345                  if len(loincs) > 0:
     346                          subst.loincs = loincs
     347
     348                  self.data = subst
     349                  return True
     350
     351          #----------------------------------------------------------------
     352 -        def _save_as_update(self):
     353                  self.data['substance'] = self._TCTRL_substance.GetValue().strip()
     354                  self.data['atc'] = self._PRW_atc.GetData()
     355                  self.data['intake_instructions'] = self._TCTRL_instructions.GetValue().strip()
     356                  success, data = self.data.save()
     357                  if not success:
     358                          err, msg = data
     359                          _log.error(err)
     360                          _log.error(msg)
     361                          self.StatusText = _('Error saving substance. %s') % msg
     362                          return False
     363
     364                  loincs = self._LCTRL_loincs.item_data
     365                  if len(loincs) > 0:
     366                          self.data.loincs = loincs
     367
     368                  return True
     369
     370          #----------------------------------------------------------------
     371 -        def _refresh_as_new(self):
     372                  self._TCTRL_substance.SetValue('')
     373                  self._PRW_atc.SetText('', None)
     374                  self._TCTRL_instructions.SetValue('')
     375                  self._PRW_loinc.SetText('', None)
     376                  self._LCTRL_loincs.set_string_items()
     377
     378                  self._TCTRL_substance.SetFocus()
     379
     380          #----------------------------------------------------------------
     381 -        def _refresh_from_existing(self):
     382                  self._TCTRL_substance.SetValue(self.data['substance'])
     383                  self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], ''), self.data['atc'])
     384                  self._TCTRL_instructions.SetValue(gmTools.coalesce(self.data['intake_instructions'], ''))
     385                  self._PRW_loinc.SetText('', None)
     386                  if len(self.data['loincs']) == 0:
     387                          self._LCTRL_loincs.set_string_items()
     388                  else:
     389                          self._LCTRL_loincs.set_string_items([ [l['loinc'], gmTools.coalesce(l['max_age_str'], ''), gmTools.coalesce(l['comment'], '')] for l in self.data['loincs' ]])
     390                          self._LCTRL_loincs.set_data([ l['loinc'] for l in self.data['loincs'] ])
     391
     392                  self._TCTRL_substance.SetFocus()
     393
     394          #----------------------------------------------------------------
     395 -        def _refresh_as_new_from_existing(self):
     396                  self._refresh_as_new()
     397
     398          #----------------------------------------------------------------
     399          # event handlers
     400          #----------------------------------------------------------------
     401 -        def _on_add_loinc_button_pressed(self, event):
     402                  event.Skip()
     403                  if (self._PRW_loinc.GetData() is None) and (self._PRW_loinc.GetValue().strip() == ''):
     404                          return
     405
     406                  if self._PRW_loinc.GetData() is None:
     407                          data = self._PRW_loinc.GetValue().strip().split(':')[0]
     408                          item = [data, '', '']
     409                  else:
     410                          data = self._PRW_loinc.GetData()
     411                          item = [data, '', '']
     412                  self._LCTRL_loincs.append_string_items_and_data([item], new_data = [data], allow_dupes = False)
     413
     414          #----------------------------------------------------------------
     415 -        def _on_remove_loincs_button_pressed(self, event):
     416                  event.Skip()
     417
     418  #------------------------------------------------------------
     419 -class cSubstancePhraseWheel(gmPhraseWheel.cPhraseWheel):
     420
     421 -        def __init__(self, *args, **kwargs):
     422
     423                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     424                  query = """
     425                          SELECT DISTINCT ON (list_label)
     426                                  list_label,
     427                                  field_label,
     428                                  data
     429                          FROM (
     430                                  SELECT
     431                                          description AS list_label,
     432                                          description AS field_label,
     433                                          pk AS data
     434                                  FROM ref.substance
     435                                  WHERE
     436                                          description %(fragment_condition)s
     437
     438                                  UNION ALL
     439
     440                                  SELECT
     441                                          term AS list_label,
     442                                          term AS field_label,
     443                                          NULL::integer AS data
     444                                  FROM ref.atc
     445                                  WHERE
     446                                          term %(fragment_condition)s
     447
     448                          ) AS candidates
     449                          ORDER BY list_label
     450                          LIMIT 50
     451                  """
     452                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
     453                  mp.setThresholds(1, 2, 4)
     454  #               mp.word_separators = '[ \t=+&:@]+'
     455                  self.SetToolTip(_('The substance name.'))
     456                  self.matcher = mp
     457                  self.selection_only = False
     458                  self.phrase_separators = None
     459
     460          #--------------------------------------------------------
     461 -        def _data2instance(self):
     462                  return gmMedication.cSubstance(aPK_obj = self.GetData(as_instance = False, can_create = False))
     463
     464  #============================================================
     465  # substance dose widgets
     466  #------------------------------------------------------------
     467 -def edit_substance_dose(parent=None, substance_dose=None, single_entry=False):
     468
     469          if substance_dose is not None:
     470                  if substance_dose.is_in_use_by_patients:
     471                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit this substance. It is in use.'), beep = True)
     472                          return False
     473
     474          ea = cSubstanceDoseEAPnl(parent, -1)
     475          ea.data = substance_dose
     476          ea.mode = gmTools.coalesce(substance_dose, 'new', 'edit')
     477          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     478          dlg.SetTitle(gmTools.coalesce(substance_dose, _('Adding new substance dose'), _('Editing substance dose')))
     479          if dlg.ShowModal() == wx.ID_OK:
     480                  dlg.DestroyLater()
     481                  return True
     482          dlg.DestroyLater()
     483          return False
     484
     485  #------------------------------------------------------------
     486 -def manage_substance_doses(parent=None, vaccine_indications_only=False):
     487
     488          if parent is None:
     489                  parent = wx.GetApp().GetTopWindow()
     490
     491          #------------------------------------------------------------
     492          def add_from_db(substance):
     493                  drug_db = get_drug_database(parent = parent)
     494                  if drug_db is None:
     495                          return False
     496                  drug_db.import_drugs()
     497                  return True
     498
     499          #------------------------------------------------------------
     500          def edit(substance_dose=None):
     501                  return edit_substance_dose(parent = parent, substance_dose = substance_dose, single_entry = (substance_dose is not None))
     502
     503          #------------------------------------------------------------
     504          def delete(substance_dose):
     505                  if substance_dose.is_in_use_by_patients:
     506                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
     507                          return False
     508                  return gmMedication.delete_substance_dose(pk_dose = substance_dose['pk_dose'])
     509
     510          #------------------------------------------------------------
     511          def refresh(lctrl):
     512                  if vaccine_indications_only:
     513                          substs = [ s for s in gmMedication.get_substance_doses(order_by = 'substance') if gmTools.coalesce(s['atc_substance'], '').startswith('J07') ]
     514                  else:
     515                          substs = gmMedication.get_substance_doses(order_by = 'substance')
     516                  items = [ [
     517                          s['substance'],
     518                          s['amount'],
     519                          s.formatted_units,
     520                          gmTools.coalesce(s['atc_substance'], ''),
     521                          gmTools.coalesce(s['intake_instructions'], ''),
     522                          s['pk_dose']
     523                  ] for s in substs ]
     524                  lctrl.set_string_items(items)
     525                  lctrl.set_data(substs)
     526
     527          #------------------------------------------------------------
     528          def get_item_tooltip(substance_dose):
     529                  if not isinstance(substance_dose, gmMedication.cSubstanceDose):
     530                          return None
     531                  return substance_dose.format(include_loincs = True)
     532
     533          #------------------------------------------------------------
     534          return gmListWidgets.get_choices_from_list (
     535                  parent = parent,
     536                  caption = _('Substance doses registered with GNUmed.'),
     537                  columns = [_('Substance'), _('Amount'), _('Unit'), 'ATC', _('Instructions'), '#'],
     538                  single_selection = False,
     539                  can_return_empty = False,
     540                  new_callback = edit,
     541                  edit_callback = edit,
     542                  delete_callback = delete,
     543                  refresh_callback = refresh,
     544                  left_extra_button = (_('Import'), _('Import substance doses from a drug database.'), add_from_db),
     545                  list_tooltip_callback = get_item_tooltip
     546          )
     547
     548  #------------------------------------------------------------
     549  from Gnumed.wxGladeWidgets import wxgSubstanceDoseEAPnl
     550
     551 -class cSubstanceDoseEAPnl(wxgSubstanceDoseEAPnl.wxgSubstanceDoseEAPnl, gmEditArea.cGenericEditAreaMixin):
     552
     553 -        def __init__(self, *args, **kwargs):
     554
     555                  try:
     556                          data = kwargs['substance']
     557                          del kwargs['substance']
     558                  except KeyError:
     559                          data = None
     560
     561                  wxgSubstanceDoseEAPnl.wxgSubstanceDoseEAPnl.__init__(self, *args, **kwargs)
     562                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     563
     564                  # Code using this mixin should set mode and data
     565                  # after instantiating the class:
     566                  self.mode = 'new'
     567                  self.data = data
     568                  if data is not None:
     569                          self.mode = 'edit'
     570
     571                  self.__init_ui()
     572
     573          #----------------------------------------------------------------
     574 -        def __init_ui(self):
     575                  self._PRW_substance.add_callback_on_modified(callback = self.__refresh_info)
     576                  self._PRW_substance.add_callback_on_selection(callback = self.__refresh_info)
     577                  self._TCTRL_amount.add_callback_on_modified(callback = self.__refresh_info)
     578                  self._PRW_unit.add_callback_on_modified(callback = self.__refresh_info)
     579                  self._PRW_unit.add_callback_on_selection(callback = self.__refresh_info)
     580                  self._PRW_dose_unit.add_callback_on_modified(callback = self.__refresh_info)
     581                  self._PRW_dose_unit.add_callback_on_selection(callback = self.__refresh_info)
     582
     583          #----------------------------------------------------------------
     584 -        def __refresh_info(self, change='dummy'):
     585                  subst = self._PRW_substance.GetValue().strip()
     586                  if subst == '':
     587                          subst = '?'
     588                  amount = self._TCTRL_amount.GetValue().strip()
     589                  if amount == '':
     590                          amount = '?'
     591                  unit = self._PRW_unit.GetValue().strip()
     592                  if unit == '':
     593                          unit = '?'
     594                  dose_unit = self._PRW_dose_unit.GetValue().strip()
     595                  if dose_unit == '':
     596                          dose_unit = _('<delivery unit>')
     597                  self._LBL_info.SetLabel('%s %s %s / %s' % (subst, amount, unit, dose_unit))
     598                  self.Refresh()
     599
     600          #----------------------------------------------------------------
     601          # generic Edit Area mixin API
     602          #----------------------------------------------------------------
     603 -        def _valid_for_save(self):
     604
     605                  validity = True
     606
     607                  if self._PRW_substance.GetValue().strip() == '':
     608                          validity = False
     609                          self._PRW_substance.display_as_valid(valid = False)
     610                          self._PRW_substance.SetFocus()
     611                  else:
     612                          self._PRW_substance.display_as_valid(valid = True)
     613
     614                  try:
     615                          decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
     616                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
     617                  except (TypeError, decimal.InvalidOperation):
     618                          validity = False
     619                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
     620                          self._TCTRL_amount.SetFocus()
     621
     622                  if self._PRW_unit.GetValue().strip() == '':
     623                          validity = False
     624                          self._PRW_unit.display_as_valid(valid = False)
     625                          self._PRW_unit.SetFocus()
     626                  else:
     627                          self._PRW_unit.display_as_valid(valid = True)
     628
     629                  if validity is False:
     630                          self.StatusText = _('Cannot save substance dose. Missing essential input.')
     631
     632                  return validity
     633
     634          #----------------------------------------------------------------
     635 -        def _save_as_new(self):
     636                  dose = gmMedication.create_substance_dose (
     637                          pk_substance = self._PRW_substance.GetData(),
     638                          substance = self._PRW_substance.GetValue().strip(),
     639                          amount = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.')),
     640                          unit = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function4value = ('strip', None)),
     641                          dose_unit = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function4value = ('strip', None))
     642                  )
     643                  success, data = dose.save()
     644                  if not success:
     645                          err, msg = data
     646                          _log.error(err)
     647                          _log.error(msg)
     648                          self.StatusText = _('Cannot create substance dose. %s') % msg
     649                          return False
     650
     651                  self.data = dose
     652                  return True
     653
     654          #----------------------------------------------------------------
     655 -        def _save_as_update(self):
     656                  #self.data['pk_substance'] = self._PRW_substance.GetData()
     657                  self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.'))
     658                  self.data['unit'] = gmTools.coalesce(self._PRW_unit.GetData(), self._PRW_unit.GetValue().strip(), function4value = ('strip', None))
     659                  self.data['dose_unit'] = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function4value = ('strip', None))
     660                  success, data = self.data.save()
     661
     662                  if not success:
     663                          err, msg = data
     664                          _log.error(err)
     665                          _log.error(msg)
     666                          self.StatusText = _('Cannot save substance dose. %s') % msg
     667                          return False
     668
     669                  return True
     670
     671          #----------------------------------------------------------------
     672 -        def _refresh_as_new(self):
     673                  self._PRW_substance.SetText('', None)
     674                  self._TCTRL_amount.SetValue('')
     675                  self._PRW_unit.SetText('', None)
     676                  self._PRW_dose_unit.SetText('', None)
     677                  self._LBL_info.SetLabel('')
     678
     679                  self._PRW_substance.SetFocus()
     680
     681          #----------------------------------------------------------------
     682 -        def _refresh_from_existing(self):
     683                  self._PRW_substance.SetText(self.data['substance'], self.data['pk_substance'])
     684                  self._PRW_substance.Disable()
     685                  self._TCTRL_amount.SetValue('%s' % self.data['amount'])
     686                  self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
     687                  self._PRW_dose_unit.SetText(gmTools.coalesce(self.data['dose_unit'], ''), self.data['dose_unit'])
     688
     689                  self._PRW_substance.SetFocus()
     690
     691          #----------------------------------------------------------------
     692 -        def _refresh_as_new_from_existing(self):
     693                  self._refresh_as_new()
     694
     695  #============================================================
     696  # drug component widgets
     697  #------------------------------------------------------------
     698 -def manage_drug_components(parent=None):
     699
     700          if parent is None:
     701                  parent = wx.GetApp().GetTopWindow()
     702
     703          #------------------------------------------------------------
     704          def edit(component=None):
     705                  substance_dose = gmMedication.cSubstanceDose(aPK_obj = component['pk_dose'])
     706                  return edit_substance_dose(parent = parent, substance_dose = substance_dose, single_entry = True)
     707
     708          #------------------------------------------------------------
     709          def delete(component):
     710                  if component.is_in_use_by_patients:
     711                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot remove this component from the drug. It is in use.'), beep = True)
     712                          return False
     713                  return component.containing_drug.remove_component(pk_component = component['pk_component'])
     714
     715          #------------------------------------------------------------
     716          def refresh(lctrl):
     717                  comps = gmMedication.get_drug_components()
     718                  items = [ [
     719                          '%s%s' % (c['substance'], gmTools.coalesce(c['atc_substance'], '', ' [%s]')),
     720                          '%s %s' % (c['amount'], c.formatted_units),
     721                          '%s%s' % (c['product'], gmTools.coalesce(c['atc_drug'], '', ' [%s]')),
     722                          c['l10n_preparation'],
     723                          gmTools.coalesce(c['external_code'], '', '%%s [%s]' % c['external_code_type']),
     724                          c['pk_component']
     725                  ] for c in comps ]
     726                  lctrl.set_string_items(items)
     727                  lctrl.set_data(comps)
     728
     729          #------------------------------------------------------------
     730          def get_item_tooltip(component):
     731                  if not isinstance(component, gmMedication.cDrugComponent):
     732                          return None
     733                  return component.format(include_loincs = True)
     734
     735          #------------------------------------------------------------
     736          gmListWidgets.get_choices_from_list (
     737                  parent = parent,
     738                  caption = _('Drug components currently known to GNUmed'),
     739                  columns = [_('Component'), _('Strength'), _('Product name'), _('Preparation'), _('Code'), '#'],
     740                  single_selection = True,
     741                  #new_callback = edit,
     742                  edit_callback = edit,
     743                  delete_callback = delete,
     744                  refresh_callback = refresh,
     745                  list_tooltip_callback = get_item_tooltip
     746          )
     747
     748  #------------------------------------------------------------
     749 -def edit_drug_component(parent=None, drug_component=None, single_entry=False):
     750          ea = cDrugComponentEAPnl(parent, -1)
     751          ea.data = drug_component
     752          ea.mode = gmTools.coalesce(drug_component, 'new', 'edit')
     753          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     754          dlg.SetTitle(gmTools.coalesce(drug_component, _('Adding new drug component'), _('Editing drug component')))
     755          if dlg.ShowModal() == wx.ID_OK:
     756                  dlg.DestroyLater()
     757                  return True
     758          dlg.DestroyLater()
     759          return False
     760
     761  #------------------------------------------------------------
     762  from Gnumed.wxGladeWidgets import wxgDrugComponentEAPnl
     763
     764 -class cDrugComponentEAPnl(wxgDrugComponentEAPnl.wxgDrugComponentEAPnl, gmEditArea.cGenericEditAreaMixin):
     765
     766 -        def __init__(self, *args, **kwargs):
     767
     768                  try:
     769                          data = kwargs['component']
     770                          del kwargs['component']
     771                  except KeyError:
     772                          data = None
     773
     774                  wxgDrugComponentEAPnl.wxgDrugComponentEAPnl.__init__(self, *args, **kwargs)
     775                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     776
     777                  # Code using this mixin should set mode and data
     778                  # after instantiating the class:
     779                  self.mode = 'new'
     780                  self.data = data
     781                  if data is not None:
     782                          self.mode = 'edit'
     783
     784                  #self.__init_ui()
     785          #----------------------------------------------------------------
     786  #       def __init_ui(self):
     787  #               # adjust phrasewheels etc
     788          #----------------------------------------------------------------
     789          # generic Edit Area mixin API
     790          #----------------------------------------------------------------
     791 -        def _valid_for_save(self):
     792                  if self.data is not None:
     793                          if self.data['is_in_use']:
     794                                  self.StatusText = _('Cannot edit drug component. It is in use.')
     795                                  return False
     796
     797                  validity = True
     798
     799                  if self._PRW_substance.GetData() is None:
     800                          validity = False
     801                          self._PRW_substance.display_as_valid(False)
     802                  else:
     803                          self._PRW_substance.display_as_valid(True)
     804
     805                  val = self._TCTRL_amount.GetValue().strip().replace(',', '.', 1)
     806                  try:
     807                          decimal.Decimal(val)
     808                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
     809                  except:
     810                          validity = False
     811                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
     812
     813                  if self._PRW_unit.GetValue().strip() == '':
     814                          validity = False
     815                          self._PRW_unit.display_as_valid(False)
     816                  else:
     817                          self._PRW_unit.display_as_valid(True)
     818
     819                  if validity is False:
     820                          self.StatusText = _('Cannot save drug component. Invalid or missing essential input.')
     821
     822                  return validity
     823          #----------------------------------------------------------------
     824 -        def _save_as_new(self):
     825                  # save the data as a new instance
     826                  data = 1
     827                  data[''] = 1
     828                  data[''] = 1
     829  #               data.save()
     830
     831                  # must be done very late or else the property access
     832                  # will refresh the display such that later field
     833                  # access will return empty values
     834  #               self.data = data
     835                  return False
     836                  return True
     837          #----------------------------------------------------------------
     838 -        def _save_as_update(self):
     839                  self.data['pk_dose'] = self._PRW_substance.GetData()
     840                  self.data['amount'] = decimal.Decimal(self._TCTRL_amount.GetValue().strip().replace(',', '.', 1))
     841                  self.data['unit'] = self._PRW_unit.GetValue().strip()
     842                  return self.data.save()
     843
     844          #----------------------------------------------------------------
     845 -        def _refresh_as_new(self):
     846                  self._TCTRL_product_name.SetValue('')
     847                  self._TCTRL_components.SetValue('')
     848                  self._TCTRL_codes.SetValue('')
     849                  self._PRW_substance.SetText('', None)
     850                  self._TCTRL_amount.SetValue('')
     851                  self._PRW_unit.SetText('', None)
     852
     853                  self._PRW_substance.SetFocus()
     854
     855          #----------------------------------------------------------------
     856 -        def _refresh_from_existing(self):
     857                  self._TCTRL_product_name.SetValue('%s (%s)' % (self.data['product'], self.data['l10n_preparation']))
     858                  self._TCTRL_components.SetValue(' / '.join(self.data.containing_drug['components']))
     859                  details = []
     860                  if self.data['atc_drug'] is not None:
     861                          details.append('ATC: %s' % self.data['atc_drug'])
     862                  if self.data['external_code_product'] is not None:
     863                          details.append('%s: %s' % (self.data['external_code_type_product'], self.data['external_code_product']))
     864                  self._TCTRL_codes.SetValue('; '.join(details))
     865
     866                  self._PRW_substance.SetText(self.data['substance'], self.data['pk_dose'])
     867                  self._TCTRL_amount.SetValue('%s' % self.data['amount'])
     868                  self._PRW_unit.SetText(self.data['unit'], self.data['unit'])
     869
     870                  self._PRW_substance.SetFocus()
     871
     872          #----------------------------------------------------------------
     873 -        def _refresh_as_new_from_existing(self):
     874                  #self._PRW_drug_product.SetText(u'', None)
     875                  #self._TCTRL_prep.SetValue(u'')
     876                  #self._TCTRL_product_name_details.SetValue(u'')
     877                  self._PRW_substance.SetText('', None)
     878                  self._TCTRL_amount.SetValue('')
     879                  self._PRW_unit.SetText('', None)
     880
     881                  self._PRW_substance.SetFocus()
     882
     883  #------------------------------------------------------------
     884 -class cDrugComponentPhraseWheel(gmPhraseWheel.cPhraseWheel):
     885
     886 -        def __init__(self, *args, **kwargs):
     887
     888                  mp = gmMedication.cDrugComponentMatchProvider()
     889                  mp.setThresholds(2, 3, 4)
     890                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     891                  self.SetToolTip(_('A drug component with optional strength.'))
     892                  self.matcher = mp
     893                  self.selection_only = False
     894          #--------------------------------------------------------
     895 -        def _data2instance(self):
     896                  return gmMedication.cDrugComponent(aPK_obj = self.GetData(as_instance = False, can_create = False))
     897
     898  #============================================================
     899  # drug products widgets
     900  #------------------------------------------------------------
     901 -def edit_drug_product(parent=None, drug_product=None, single_entry=False):
     902
     903          if drug_product is not None:
     904                  if drug_product.is_in_use_by_patients:
     905                          gmGuiHelpers.gm_show_info (
     906                                  aTitle = _('Editing drug'),
     907                                  aMessage = _(
     908                                          'Cannot edit the drug product\n'
     909                                          '\n'
     910                                          ' "%s" (%s)\n'
     911                                          '\n'
     912                                          'because it is currently taken by patients.\n'
     913                                  ) % (drug_product['product'], drug_product['l10n_preparation'])
     914                          )
     915                          return False
     916
     917          if parent is None:
     918                  parent = wx.GetApp().GetTopWindow()
     919
     920          #--------------------------------------------
     921          def manage_substances(drug):
     922                  manage_substance_doses(parent = parent)
     923
     924          #--------------------------------------------
     925          ea = cDrugProductEAPnl(parent, -1)
     926          ea.data = drug_product
     927          ea.mode = gmTools.coalesce(drug_product, 'new', 'edit')
     928          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     929          dlg.SetTitle(gmTools.coalesce(drug_product, _('Adding new drug product'), _('Editing drug product')))
     930          dlg.left_extra_button = (
     931                  _('Substances'),
     932                  _('Manage substances'),
     933                  manage_substances
     934          )
     935          if dlg.ShowModal() == wx.ID_OK:
     936                  dlg.DestroyLater()
     937                  return True
     938          dlg.DestroyLater()
     939          return False
     940
     941  #------------------------------------------------------------
     942 -def manage_drug_products(parent=None, ignore_OK_button=False):
     943
     944          if parent is None:
     945                  parent = wx.GetApp().GetTopWindow()
     946
     947          #------------------------------------------------------------
     948          def add_from_db(drug):
     949                  drug_db = get_drug_database(parent = parent)
     950                  if drug_db is None:
     951                          return False
     952                  drug_db.import_drugs()
     953                  return True
     954
     955          #------------------------------------------------------------
     956          def get_tooltip(product=None):
     957                  return product.format(include_component_details = True)
     958
     959          #------------------------------------------------------------
     960          def edit(product):
     961                  if product is not None:
     962                          if product.is_in_use_as_vaccine:
     963                                  gmGuiHelpers.gm_show_info (
     964                                          aTitle = _('Editing medication'),
     965                                          aMessage = _(
     966                                                  'Cannot edit the vaccine product\n'
     967                                                  '\n'
     968                                                  ' "%s" (%s)\n'
     969                                                  '\n'
     970                                                  'because it is in use.'
     971                                          ) % (product['product'], product['l10n_preparation'])
     972                                  )
     973                                  return False
     974
     975                  return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
     976
     977          #------------------------------------------------------------
     978          def delete(product):
     979                  if not product.delete_associated_vaccine():
     980                          gmGuiHelpers.gm_show_info (
     981                                  aTitle = _('Deleting vaccine'),
     982                                  aMessage = _(
     983                                          'Cannot delete the vaccine product\n'
     984                                          '\n'
     985                                          ' "%s" (%s)\n'
     986                                          '\n'
     987                                          'because it is in use.'
     988                                  ) % (product['product'], product['l10n_preparation'])
     989                          )
     990                          return False
     991
     992                  gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
     993                  return True
     994
     995          #------------------------------------------------------------
     996          def new():
     997                  return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
     998
     999          #------------------------------------------------------------
    1000          def refresh(lctrl):
    1001                  drugs = gmMedication.get_drug_products()
    1002                  items = [ [
    1003                          '%s%s' % (
    1004                                  d['product'],
    1005                                  gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
    1006                          ),
    1007                          d['l10n_preparation'],
    1008                          gmTools.coalesce(d['atc'], ''),
    1009                          '; '.join([ '%s %s%s' % (
    1010                                  c['substance'],
    1011                                  c['amount'],
    1012                                  gmMedication.format_units(c['unit'], c['dose_unit'])
    1013                          ) for c in d['components']]),
    1014                          gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
    1015                          d['pk_drug_product']
    1016                  ] for d in drugs ]
    1017                  lctrl.set_string_items(items)
    1018                  lctrl.set_data(drugs)
    1019
    1020          #------------------------------------------------------------
    1021          gmListWidgets.get_choices_from_list (
    1022                  parent = parent,
    1023                  caption = _('Drug products currently known to GNUmed.'),
    1024                  columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
    1025                  single_selection = True,
    1026                  ignore_OK_button = ignore_OK_button,
    1027                  refresh_callback = refresh,
    1028                  new_callback = new,
    1029                  edit_callback = edit,
    1030                  delete_callback = delete,
    1031                  list_tooltip_callback = get_tooltip,
    1032                  left_extra_button = (_('Import'), _('Import substances and products from a drug database.'), add_from_db)
    1033                  #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing)
    1034                  #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients)
    1035          )
    1036
    1037  #------------------------------------------------------------
    1038 -def manage_components_of_drug_product(parent=None, product=None):
    1039
    1040          if product is not None:
    1041                  if product.is_in_use_by_patients:
    1042                          gmGuiHelpers.gm_show_info (
    1043                                  aTitle = _('Managing components of a drug'),
    1044                                  aMessage = _(
    1045                                          'Cannot manage the components of the drug product\n'
    1046                                          '\n'
    1047                                          ' "%s" (%s)\n'
    1048                                          '\n'
    1049                                          'because it is currently taken by patients.\n'
    1050                                  ) % (product['product'], product['l10n_preparation'])
    1051                          )
    1052                          return False
    1053
    1054          #--------------------------------------------------------
    1055          if parent is None:
    1056                  parent = wx.GetApp().GetTopWindow()
    1057
    1058          #--------------------------------------------------------
    1059  #       def manage_substances():
    1060  #               pass
    1061
    1062          #--------------------------------------------------------
    1063          if product is None:
    1064                  msg = _('Pick the substance doses which are components of this drug.')
    1065                  right_col = _('Components of drug')
    1066                  comp_doses = []
    1067          else:
    1068                  right_col = '%s (%s)' % (product['product'], product['l10n_preparation'])
    1069                  msg = _(
    1070                          'Adjust the components of "%s"\n'
    1071                          '\n'
    1072                          'The drug must contain at least one component. Any given\n'
    1073                          'substance can only be included once per drug.'
    1074                  ) % right_col
    1075                  comp_doses = [ comp.substance_dose for comp in product.components ]
    1076
    1077          doses = gmMedication.get_substance_doses(order_by = 'substance')
    1078          choices = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in doses ]
    1079          picks = [ '%s %s %s' % (d['substance'], d['amount'], d.formatted_units) for d in comp_doses ]
    1080
    1081          picker = gmListWidgets.cItemPickerDlg (
    1082                  parent,
    1083                  -1,
    1084                  title = _('Managing components of a drug ...'),
    1085                  msg = msg
    1086          )
    1087          picker.set_columns(['Substance doses'], [right_col])
    1088          picker.set_choices(choices = choices, data = doses)
    1089          picker.set_picks(picks = picks, data = comp_doses)
    1090  #       picker.extra_button = (
    1091  #               _('Substances'),
    1092  #               _('Manage list of substances'),
    1093  #               manage_substances
    1094  #       )
    1095
    1096          btn_pressed = picker.ShowModal()
    1097          doses2set = picker.get_picks()
    1098          picker.DestroyLater()
    1099
    1100          if btn_pressed != wx.ID_OK:
    1101                  return (False, None)
    1102
    1103          if product is not None:
    1104                  product.set_substance_doses_as_components(substance_doses = doses2set)
    1105
    1106          return (True, doses2set)
    1107
    1108  #------------------------------------------------------------
    1109  from Gnumed.wxGladeWidgets import wxgDrugProductEAPnl
    1110
    1111 -class cDrugProductEAPnl(wxgDrugProductEAPnl.wxgDrugProductEAPnl, gmEditArea.cGenericEditAreaMixin):
    1112
    1113 -        def __init__(self, *args, **kwargs):
    1114                  try:
    1115                          data = kwargs['drug']
    1116                          del kwargs['drug']
    1117                  except KeyError:
    1118                          data = None
    1119
    1120                  wxgDrugProductEAPnl.wxgDrugProductEAPnl.__init__(self, *args, **kwargs)
    1121                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1122
    1123                  self.mode = 'new'
    1124                  self.data = data
    1125                  if data is not None:
    1126                          self.mode = 'edit'
    1127                          self.__component_doses = data.components_as_doses
    1128
    1129                  #self.__init_ui()
    1130          #----------------------------------------------------------------
    1131  #       def __init_ui(self):
    1132                  # adjust external type PRW
    1133          #----------------------------------------------------------------
    1134          # generic Edit Area mixin API
    1135          #----------------------------------------------------------------
    1136 -        def _valid_for_save(self):
    1137
    1138                  if self.data is not None:
    1139                          if self.data.is_in_use_by_patients:
    1140                                  self.StatusText = _('Cannot edit drug product. It is in use.')
    1141                                  return False
    1142
    1143                  validity = True
    1144
    1145                  product_name = self._PRW_product_name.GetValue().strip()
    1146                  if product_name == '':
    1147                          validity = False
    1148                          self._PRW_product_name.display_as_valid(False)
    1149                  else:
    1150                          self._PRW_product_name.display_as_valid(True)
    1151
    1152                  preparation = self._PRW_preparation.GetValue().strip()
    1153                  if preparation == '':
    1154                          validity = False
    1155                          self._PRW_preparation.display_as_valid(False)
    1156                  else:
    1157                          self._PRW_preparation.display_as_valid(True)
    1158
    1159                  if validity is True:
    1160                          # dupe ?
    1161                          drug = gmMedication.get_drug_by_name(product_name = product_name, preparation = preparation)
    1162                          if drug is not None:
    1163                                  if self.mode != 'edit':
    1164                                          validity = False
    1165                                          self._PRW_product_name.display_as_valid(False)
    1166                                          self._PRW_preparation.display_as_valid(False)
    1167                                          gmGuiHelpers.gm_show_error (
    1168                                                  title = _('Checking product data'),
    1169                                                  error = _(
    1170                                                          'The information you entered:\n'
    1171                                                          '\n'
    1172                                                          ' [%s %s]\n'
    1173                                                          '\n'
    1174                                                          'already exists as a drug product.'
    1175                                                  ) % (product_name, preparation)
    1176                                          )
    1177                          else:
    1178                                  # lacking components ?
    1179                                  self._TCTRL_components.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND))
    1180                                  if len(self.__component_doses) == 0:
    1181                                          wants_empty = gmGuiHelpers.gm_show_question (
    1182                                                  title = _('Checking product data'),
    1183                                                  question = _(
    1184                                                          'You have not selected any substances\n'
    1185                                                          'as drug components.\n'
    1186                                                          '\n'
    1187                                                          'Without components you will not be able to\n'
    1188                                                          'use this drug for documenting patient care.\n'
    1189                                                          '\n'
    1190                                                          'Are you sure you want to save\n'
    1191                                                          'it without components ?'
    1192                                                  )
    1193                                          )
    1194                                          if not wants_empty:
    1195                                                  validity = False
    1196                                                  self.display_ctrl_as_valid(ctrl = self._TCTRL_components, valid = False)
    1197
    1198                  if validity is False:
    1199                          self.StatusText = _('Cannot save drug product. Invalid or missing essential input.')
    1200
    1201                  return validity
    1202
    1203          #----------------------------------------------------------------
    1204 -        def _save_as_new(self):
    1205
    1206                  drug = gmMedication.create_drug_product (
    1207                          product_name = self._PRW_product_name.GetValue().strip(),
    1208                          preparation = gmTools.coalesce (
    1209                                  self._PRW_preparation.GetData(),
    1210                                  self._PRW_preparation.GetValue()
    1211                          ).strip(),
    1212                          doses = self.__component_doses,
    1213                          return_existing = True
    1214                  )
    1215                  drug['is_fake_product'] = self._CHBOX_is_fake.GetValue()
    1216                  drug['atc'] = self._PRW_atc.GetData()
    1217                  code = self._TCTRL_external_code.GetValue().strip()
    1218                  if code != '':
    1219                          drug['external_code'] = code
    1220                          drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
    1221
    1222                  drug.save()
    1223
    1224  #               if len(self.__component_doses) > 0:
    1225  #                       drug.set_substance_doses_as_components(substance_doses = self.__component_doses)
    1226
    1227                  self.data = drug
    1228
    1229                  return True
    1230
    1231          #----------------------------------------------------------------
    1232 -        def _save_as_update(self):
    1233                  self.data['product'] = self._PRW_product_name.GetValue().strip()
    1234                  self.data['preparation'] = gmTools.coalesce (
    1235                          self._PRW_preparation.GetData(),
    1236                          self._PRW_preparation.GetValue()
    1237                  ).strip()
    1238                  self.data['is_fake_product'] = self._CHBOX_is_fake.GetValue()
    1239                  self.data['atc'] = self._PRW_atc.GetData()
    1240                  code = self._TCTRL_external_code.GetValue().strip()
    1241                  if code != '':
    1242                          self.data['external_code'] = code
    1243                          self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
    1244                  success, data = self.data.save()
    1245                  if not success:
    1246                          err, msg = data
    1247                          _log.error('problem saving')
    1248                          _log.error('%s', err)
    1249                          _log.error('%s', msg)
    1250                  return (success is True)
    1251
    1252          #----------------------------------------------------------------
    1253 -        def _refresh_as_new(self):
    1254                  self._PRW_product_name.SetText('', None)
    1255                  self._PRW_preparation.SetText('', None)
    1256                  self._CHBOX_is_fake.SetValue(False)
    1257                  self._TCTRL_components.SetValue('')
    1258                  self._PRW_atc.SetText('', None)
    1259                  self._TCTRL_external_code.SetValue('')
    1260                  self._PRW_external_code_type.SetText('', None)
    1261
    1262                  self._PRW_product_name.SetFocus()
    1263
    1264                  self.__component_substances = []
    1265
    1266          #----------------------------------------------------------------
    1267 -        def _refresh_as_new_from_existing(self):
    1268                  self._refresh_as_new()
    1269
    1270          #----------------------------------------------------------------
    1271 -        def _refresh_from_existing(self):
    1272                  self._PRW_product_name.SetText(self.data['product'], self.data['pk_drug_product'])
    1273                  self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
    1274                  self._CHBOX_is_fake.SetValue(self.data['is_fake_product'])
    1275                  comp_str = ''
    1276                  if len(self.data['components']) > 0:
    1277                          comp_str = '- %s' % '\n- '.join([ '%s %s%s' % (c['substance'], c['amount'], gmMedication.format_units(c['unit'], c['dose_unit'])) for c in self.data['components'] ])
    1278                  self._TCTRL_components.SetValue(comp_str)
    1279                  self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], ''), self.data['atc'])
    1280                  self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], ''))
    1281                  t = gmTools.coalesce(self.data['external_code_type'], '')
    1282                  self._PRW_external_code_type.SetText(t, t)
    1283
    1284                  self._PRW_product_name.SetFocus()
    1285
    1286                  self.__component_doses = self.data.components_as_doses
    1287
    1288          #----------------------------------------------------------------
    1289          # event handler
    1290          #----------------------------------------------------------------
    1291 -        def _on_manage_components_button_pressed(self, event):
    1292                  event.Skip()
    1293                  if self.mode == 'new_from_existing':
    1294                          product = None
    1295                  else:
    1296                          product = self.data
    1297                  OKed, doses = manage_components_of_drug_product(parent = self, product = product)
    1298                  if OKed is True:
    1299                          self.__component_doses = doses
    1300                          comp_str = ''
    1301                          if len(doses) > 0:
    1302                                  comp_str = '- %s' % '\n- '.join([ '%s %s%s' % (d['substance'], d['amount'], gmMedication.format_units(d['unit'], d['dose_unit'])) for d in doses ])
    1303                          self._TCTRL_components.SetValue(comp_str)
    1304
    1305  #------------------------------------------------------------
    1306 -class cDrugProductPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1307
    1308 -        def __init__(self, *args, **kwargs):
    1309
    1310                  query = """
    1311                          SELECT
    1312                                  pk
    1313                                          AS data,
    1314                                  (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
    1315                                          AS list_label,
    1316                                  (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
    1317                                          AS field_label
    1318                          FROM ref.drug_product
    1319                          WHERE description %(fragment_condition)s
    1320                          ORDER BY list_label
    1321                          LIMIT 50"""
    1322
    1323                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
    1324                  mp.setThresholds(2, 3, 4)
    1325                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
    1326                  self.SetToolTip(_(
    1327                          'The product name of the drug.\n'
    1328                          '\n'
    1329                          'Note: a product name will need to be linked to\n'
    1330                          'one or more components before it can be used,\n'
    1331                          'except in the case of fake (generic) vaccines.'
    1332                  ))
    1333                  self.matcher = mp
    1334                  self.selection_only = False
    1335
    1336  #============================================================
    1337  # single-component generic drugs
    1338  # drug name is forced to substance + amount + unit + dose_unit
    1339  #------------------------------------------------------------
    1340 -def edit_single_component_generic_drug(parent=None, drug=None, single_entry=False, fields=None, return_drug=False):
    1341
    1342  #       if drug is not None:
    1343  #               if drug.is_in_use_by_patients:
    1344  #                       gmGuiHelpers.gm_show_info (
    1345  #                               aTitle = _('Editing single-component generic drug'),
    1346  #                               aMessage = _(
    1347  #                                       'Cannot edit the single-component generic drug\n'
    1348  #                                       '\n'
    1349  #                                       ' "%s" (%s)\n'
    1350  #                                       '\n'
    1351  #                                       'because it is currently taken by patients.\n'
    1352  #                               ) % (drug['product'], drug['l10n_preparation'])
    1353  #                       )
    1354  #                       return False
    1355
    1356          if parent is None:
    1357                  parent = wx.GetApp().GetTopWindow()
    1358
    1359  #       #--------------------------------------------
    1360  #       def manage_substances(drug):
    1361  #               manage_substance_doses(parent = parent)
    1362
    1363          #--------------------------------------------
    1364          ea = cSingleComponentGenericDrugEAPnl(parent, -1)
    1365          ea.data = drug
    1366          ea.mode = gmTools.coalesce(drug, 'new', 'edit')
    1367          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
    1368          if fields is not None:
    1369                  ea.set_fields(fields)
    1370          dlg.SetTitle(gmTools.coalesce(drug, _('Adding new single-component generic drug'), _('Editing single-component generic drug')))
    1371  #       dlg.left_extra_button = (
    1372  #               _('Substances'),
    1373  #               _('Manage substances'),
    1374  #               manage_substances
    1375  #       )
    1376          if dlg.ShowModal() == wx.ID_OK:
    1377                  drug = ea.data
    1378                  dlg.DestroyLater()
    1379                  if return_drug:
    1380                          return drug
    1381                  return True
    1382          dlg.DestroyLater()
    1383          if return_drug:
    1384                  return None
    1385          return False
    1386
    1387  #------------------------------------------------------------
    1388 -def manage_single_component_generic_drugs(parent=None, ignore_OK_button=False):
    1389
    1390          if parent is None:
    1391                  parent = wx.GetApp().GetTopWindow()
    1392
    1393          #------------------------------------------------------------
    1394          def add_from_db(drug):
    1395                  drug_db = get_drug_database(parent = parent)
    1396                  if drug_db is None:
    1397                          return False
    1398                  drug_db.import_drugs()
    1399                  return True
    1400
    1401          #------------------------------------------------------------
    1402          def get_tooltip(product=None):
    1403                  return product.format(include_component_details = True)
    1404
    1405          #------------------------------------------------------------
    1406          def edit(product):
    1407                  if product is not None:
    1408                          if product.is_vaccine:
    1409                                  gmGuiHelpers.gm_show_info (
    1410                                          aTitle = _('Editing medication'),
    1411                                          aMessage = _(
    1412                                                  'Cannot edit the medication\n'
    1413                                                  '\n'
    1414                                                  ' "%s" (%s)\n'
    1415                                                  '\n'
    1416                                                  'because it is a vaccine. Please edit it\n'
    1417                                                  'from the vaccine management section !\n'
    1418                                          ) % (product['product'], product['l10n_preparation'])
    1419                                  )
    1420                                  return False
    1421
    1422                  return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
    1423
    1424          #------------------------------------------------------------
    1425          def delete(product):
    1426                  if product.is_vaccine:
    1427                          gmGuiHelpers.gm_show_info (
    1428                                  aTitle = _('Deleting medication'),
    1429                                  aMessage = _(
    1430                                          'Cannot delete the medication\n'
    1431                                          '\n'
    1432                                          ' "%s" (%s)\n'
    1433                                          '\n'
    1434                                          'because it is a vaccine. Please delete it\n'
    1435                                          'from the vaccine management section !\n'
    1436                                  ) % (product['product'], product['l10n_preparation'])
    1437                          )
    1438                          return False
    1439                  gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
    1440                  return True
    1441
    1442          #------------------------------------------------------------
    1443          def new():
    1444                  return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
    1445
    1446          #------------------------------------------------------------
    1447          def refresh(lctrl):
    1448                  drugs = gmMedication.get_drug_products()
    1449                  items = [ [
    1450                          '%s%s' % (
    1451                                  d['product'],
    1452                                  gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
    1453                          ),
    1454                          d['l10n_preparation'],
    1455                          gmTools.coalesce(d['atc'], ''),
    1456                          '; '.join([ '%s %s%s' % (
    1457                                  c['substance'],
    1458                                  c['amount'],
    1459                                  gmMedication.format_units(c['unit'], c['dose_unit'])
    1460                          ) for c in d['components']]),
    1461                          gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
    1462                          d['pk_drug_product']
    1463                  ] for d in drugs ]
    1464                  lctrl.set_string_items(items)
    1465                  lctrl.set_data(drugs)
    1466
    1467          #------------------------------------------------------------
    1468          gmListWidgets.get_choices_from_list (
    1469                  parent = parent,
    1470                  caption = _('Drug products currently known to GNUmed.'),
    1471                  columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
    1472                  single_selection = True,
    1473                  ignore_OK_button = ignore_OK_button,
    1474                  refresh_callback = refresh,
    1475                  new_callback = new,
    1476                  edit_callback = edit,
    1477                  delete_callback = delete,
    1478                  list_tooltip_callback = get_tooltip,
    1479                  #left_extra_button = (_('Import'), _('Import substances and products from a drug database.'), add_from_db)
    1480                  #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing)
    1481                  #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients)
    1482          )
    1483
    1484  #------------------------------------------------------------
    1485  from Gnumed.wxGladeWidgets import wxgSingleComponentGenericDrugEAPnl
    1486
    1487 -class cSingleComponentGenericDrugEAPnl(wxgSingleComponentGenericDrugEAPnl.wxgSingleComponentGenericDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
    1488
    1489 -        def __init__(self, *args, **kwargs):
    1490
    1491                  try:
    1492                          data = kwargs['drug']
    1493                          del kwargs['drug']
    1494                  except KeyError:
    1495                          data = None
    1496
    1497                  wxgSingleComponentGenericDrugEAPnl.wxgSingleComponentGenericDrugEAPnl.__init__(self, *args, **kwargs)
    1498                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1499
    1500                  self.mode = 'new'
    1501                  self.data = data
    1502                  if data is not None:
    1503                          self.mode = 'edit'
    1504
    1505                  self.__init_ui()
    1506
    1507          #----------------------------------------------------------------
    1508 -        def __init_ui(self):
    1509                  self._PRW_substance.add_callback_on_modified(callback = self._on_name_field_modified)
    1510                  self._PRW_substance.add_callback_on_selection(callback = self._on_name_field_modified)
    1511                  self._TCTRL_amount.add_callback_on_modified(callback = self._on_name_field_modified)
    1512                  self._PRW_unit.add_callback_on_modified(callback = self._on_name_field_modified)
    1513                  self._PRW_unit.add_callback_on_selection(callback = self._on_name_field_modified)
    1514                  self._PRW_dose_unit.add_callback_on_modified(callback = self._on_name_field_modified)
    1515                  self._PRW_dose_unit.add_callback_on_selection(callback = self._on_name_field_modified)
    1516
    1517          #----------------------------------------------------------------
    1518          # generic Edit Area mixin API
    1519          #----------------------------------------------------------------
    1520 -        def _valid_for_save(self):
    1521
    1522                  validity = True
    1523
    1524                  if self._PRW_preparation.Value.strip() == '':
    1525                          validity = False
    1526                          self._PRW_preparation.display_as_valid(False)
    1527                          self.StatusText = _('Drug form is missing.')
    1528                          self._PRW_preparation.SetFocus()
    1529                  else:
    1530                          self._PRW_preparation.display_as_valid(True)
    1531
    1532                  if self._PRW_unit.GetData() is None:
    1533                          validity = False
    1534                          self._PRW_unit.display_as_valid(False)
    1535                          self.StatusText = _('Unit for amount is missing.')
    1536                          self._PRW_unit.SetFocus()
    1537                  else:
    1538                          self._PRW_unit.display_as_valid(True)
    1539
    1540                  if self._TCTRL_amount.GetValue().strip() == '':
    1541                          validity = False
    1542                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
    1543                          self.StatusText = _('Amount is missing.')
    1544                          self._TCTRL_amount.SetFocus()
    1545                  else:
    1546                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
    1547
    1548                  if self._PRW_substance.GetData() is None:
    1549                          val = self._PRW_substance.Value.strip()
    1550                          if val != '' and gmATC.exists_as_atc(val):
    1551                                  subst = gmMedication.create_substance(substance = val)
    1552                                  self._PRW_substance.SetText(subst['substance'], subst['pk_substance'])
    1553                                  self._PRW_substance.display_as_valid(True)
    1554                          else:
    1555                                  validity = False
    1556                                  self._PRW_substance.display_as_valid(False)
    1557                                  self.StatusText = _('Substance is missing.')
    1558                                  self._PRW_substance.SetFocus()
    1559                  else:
    1560                          self._PRW_substance.display_as_valid(True)
    1561
    1562                  return validity
    1563
    1564          #----------------------------------------------------------------
    1565 -        def _save_as_new(self):
    1566
    1567                  dose = gmMedication.create_substance_dose (
    1568                          pk_substance = self._PRW_substance.GetData(),
    1569                          amount = self._TCTRL_amount.Value.strip(),
    1570                          unit = self._PRW_unit.GetData(),
    1571                          dose_unit = self._PRW_dose_unit.GetData()
    1572                  )
    1573                  dose_unit = self._PRW_dose_unit.GetValue().strip()
    1574                  if dose_unit != '':
    1575                          dose_unit = '/' + dose_unit
    1576                  name = '%s %s%s%s' % (
    1577                          self._PRW_substance.GetValue().strip(),
    1578                          self._TCTRL_amount.Value.strip(),
    1579                          self._PRW_unit.GetValue().strip(),
    1580                          dose_unit
    1581                  )
    1582                  drug = gmMedication.create_drug_product (
    1583                          product_name = name,
    1584                          preparation = self._PRW_preparation.GetValue().strip(),
    1585                          return_existing = True,
    1586                          doses = [dose]
    1587                  )
    1588                  drug['is_fake_product'] = True
    1589                  drug.save()
    1590                  self.data = drug
    1591                  return True
    1592
    1593          #----------------------------------------------------------------
    1594 -        def _save_as_update(self):
    1595                  return False
    1596  #               self.data[''] = self._CHBOX_xxx.GetValue()
    1597  #               self.data.save()
    1598  #               return True
    1599
    1600          #----------------------------------------------------------------
    1601 -        def _refresh_as_new(self):
    1602                  self._LBL_drug_name.SetLabel('')
    1603                  self._PRW_substance.SetText('', None)
    1604                  self._TCTRL_amount.SetValue('')
    1605                  self._PRW_unit.SetText('', None)
    1606                  self._PRW_dose_unit.SetText('', None)
    1607                  self._PRW_preparation.SetText('', None)
    1608
    1609          #----------------------------------------------------------------
    1610 -        def _refresh_as_new_from_existing(self):
    1611                  self._refresh_as_new()
    1612
    1613          #----------------------------------------------------------------
    1614 -        def _refresh_from_existing(self):
    1615                  pass
    1616
    1617          #----------------------------------------------------------------
    1618 -        def set_fields(self, fields):
    1619                  try:
    1620                          self._PRW_substance.SetText(fields['substance']['value'], fields['substance']['data'])
    1621                  except KeyError:
    1622                          _log.error('cannot set field [substance] from <%s>', fields)
    1623
    1624          #----------------------------------------------------------------
    1625 -        def _on_name_field_modified(self, data=None):
    1626                  dose_unit = self._PRW_dose_unit.GetValue().strip()
    1627                  if dose_unit != '':
    1628                          dose_unit = '/' + dose_unit
    1629                  name = '%s %s%s%s' % (
    1630                          self._PRW_substance.GetValue().strip(),
    1631                          self._TCTRL_amount.Value.strip(),
    1632                          self._PRW_unit.GetValue().strip(),
    1633                          dose_unit
    1634                  )
    1635                  self._LBL_drug_name.SetLabel('"%s"' % name.strip())
    1636
    1637  #============================================================
    1638  # main
    1639  #------------------------------------------------------------
    1640  if __name__ == '__main__':
    1641
    1642          if len(sys.argv) < 2:
    1643                  sys.exit()
    1644
    1645          if sys.argv[1] != 'test':
    1646                  sys.exit()
    1647
    1648          from Gnumed.business import gmPersonSearch
    1649
    1650          pat = gmPersonSearch.ask_for_patient()
    1651          if pat is None:
    1652                  sys.exit()
    1653          gmPerson.set_active_patient(patient = pat)
    1654
    1655          #----------------------------------------
    1656          app = wx.PyWidgetTester(size = (600, 300))
    1657          app.SetWidget(cSubstancePhraseWheel, -1)
    1658          app.MainLoop()
    1659          #manage_substance_intakes()
    1660          edit_single_component_generic_drug (
    1661                  single_entry = True,
    1662                  fields = {'substance': {'value': val, 'data': None}},
    1663                  return_drug = True
    1664          )
    1665