Gnumed.wxpython.gmSubstanceMgmtWidgets

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.Destroy()
     220                  return True
     221          dlg.Destroy()
     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.status_message = _('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.status_message = _('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.status_message = _('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.Destroy()
     481                  return True
     482          dlg.Destroy()
     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.status_message = _('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(), function_initial = ('strip', None)),
     641                          dose_unit = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function_initial = ('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.status_message = _('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(), function_initial = ('strip', None))
     659                  self.data['dose_unit'] = gmTools.coalesce(self._PRW_dose_unit.GetData(), self._PRW_dose_unit.GetValue().strip(), function_initial = ('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.status_message = _('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.Destroy()
     757                  return True
     758          dlg.Destroy()
     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.status_message = _('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.status_message = _('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.Destroy()
     937                  return True
     938          dlg.Destroy()
     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.Destroy()
    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.status_message = _('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.status_message = _('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                          return_existing = True
    1213                  )
    1214                  drug['is_fake_product'] = self._CHBOX_is_fake.GetValue()
    1215                  drug['atc'] = self._PRW_atc.GetData()
    1216                  code = self._TCTRL_external_code.GetValue().strip()
    1217                  if code != '':
    1218                          drug['external_code'] = code
    1219                          drug['external_code_type'] = self._PRW_external_code_type.GetData().strip()
    1220
    1221                  drug.save()
    1222
    1223                  if len(self.__component_doses) > 0:
    1224                          drug.set_substance_doses_as_components(substance_doses = self.__component_doses)
    1225
    1226                  self.data = drug
    1227
    1228                  return True
    1229
    1230          #----------------------------------------------------------------
    1231 -        def _save_as_update(self):
    1232                  self.data['product'] = self._PRW_product_name.GetValue().strip()
    1233                  self.data['preparation'] = gmTools.coalesce (
    1234                          self._PRW_preparation.GetData(),
    1235                          self._PRW_preparation.GetValue()
    1236                  ).strip()
    1237                  self.data['is_fake_product'] = self._CHBOX_is_fake.GetValue()
    1238                  self.data['atc'] = self._PRW_atc.GetData()
    1239                  code = self._TCTRL_external_code.GetValue().strip()
    1240                  if code != '':
    1241                          self.data['external_code'] = code
    1242                          self.data['external_code_type'] = self._PRW_external_code_type.GetData().strip()
    1243                  success, data = self.data.save()
    1244                  if not success:
    1245                          err, msg = data
    1246                          _log.error('problem saving')
    1247                          _log.error('%s', err)
    1248                          _log.error('%s', msg)
    1249                  return (success is True)
    1250
    1251          #----------------------------------------------------------------
    1252 -        def _refresh_as_new(self):
    1253                  self._PRW_product_name.SetText('', None)
    1254                  self._PRW_preparation.SetText('', None)
    1255                  self._CHBOX_is_fake.SetValue(False)
    1256                  self._TCTRL_components.SetValue('')
    1257                  self._PRW_atc.SetText('', None)
    1258                  self._TCTRL_external_code.SetValue('')
    1259                  self._PRW_external_code_type.SetText('', None)
    1260
    1261                  self._PRW_product_name.SetFocus()
    1262
    1263                  self.__component_substances = []
    1264
    1265          #----------------------------------------------------------------
    1266 -        def _refresh_as_new_from_existing(self):
    1267                  self._refresh_as_new()
    1268
    1269          #----------------------------------------------------------------
    1270 -        def _refresh_from_existing(self):
    1271                  self._PRW_product_name.SetText(self.data['product'], self.data['pk_drug_product'])
    1272                  self._PRW_preparation.SetText(self.data['preparation'], self.data['preparation'])
    1273                  self._CHBOX_is_fake.SetValue(self.data['is_fake_product'])
    1274                  comp_str = ''
    1275                  if len(self.data['components']) > 0:
    1276                          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'] ])
    1277                  self._TCTRL_components.SetValue(comp_str)
    1278                  self._PRW_atc.SetText(gmTools.coalesce(self.data['atc'], ''), self.data['atc'])
    1279                  self._TCTRL_external_code.SetValue(gmTools.coalesce(self.data['external_code'], ''))
    1280                  t = gmTools.coalesce(self.data['external_code_type'], '')
    1281                  self._PRW_external_code_type.SetText(t, t)
    1282
    1283                  self._PRW_product_name.SetFocus()
    1284
    1285                  self.__component_doses = self.data.components_as_doses
    1286
    1287          #----------------------------------------------------------------
    1288          # event handler
    1289          #----------------------------------------------------------------
    1290 -        def _on_manage_components_button_pressed(self, event):
    1291                  event.Skip()
    1292                  if self.mode == 'new_from_existing':
    1293                          product = None
    1294                  else:
    1295                          product = self.data
    1296                  OKed, doses = manage_components_of_drug_product(parent = self, product = product)
    1297                  if OKed is True:
    1298                          self.__component_doses = doses
    1299                          comp_str = ''
    1300                          if len(doses) > 0:
    1301                                  comp_str = '- %s' % '\n- '.join([ '%s %s%s' % (d['substance'], d['amount'], gmMedication.format_units(d['unit'], d['dose_unit'])) for d in doses ])
    1302                          self._TCTRL_components.SetValue(comp_str)
    1303
    1304  #------------------------------------------------------------
    1305 -class cDrugProductPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1306
    1307 -        def __init__(self, *args, **kwargs):
    1308
    1309                  query = """
    1310                          SELECT
    1311                                  pk
    1312                                          AS data,
    1313                                  (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
    1314                                          AS list_label,
    1315                                  (description || ' (' || preparation || ')' || coalesce(' [' || atc_code || ']', ''))
    1316                                          AS field_label
    1317                          FROM ref.drug_product
    1318                          WHERE description %(fragment_condition)s
    1319                          ORDER BY list_label
    1320                          LIMIT 50"""
    1321
    1322                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
    1323                  mp.setThresholds(2, 3, 4)
    1324                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
    1325                  self.SetToolTip(_(
    1326                          'The product name of the drug.\n'
    1327                          '\n'
    1328                          'Note: a product name will need to be linked to\n'
    1329                          'one or more components before it can be used,\n'
    1330                          'except in the case of fake (generic) vaccines.'
    1331                  ))
    1332                  self.matcher = mp
    1333                  self.selection_only = False
    1334
    1335  #============================================================
    1336  # single-component generic drugs
    1337  # drug name is forced to substance + amount + unit + dose_unit
    1338  #------------------------------------------------------------
    1339 -def edit_single_component_generic_drug(parent=None, drug=None, single_entry=False, fields=None, return_drug=False):
    1340
    1341  #       if drug is not None:
    1342  #               if drug.is_in_use_by_patients:
    1343  #                       gmGuiHelpers.gm_show_info (
    1344  #                               aTitle = _('Editing single-component generic drug'),
    1345  #                               aMessage = _(
    1346  #                                       'Cannot edit the single-component generic drug\n'
    1347  #                                       '\n'
    1348  #                                       ' "%s" (%s)\n'
    1349  #                                       '\n'
    1350  #                                       'because it is currently taken by patients.\n'
    1351  #                               ) % (drug['product'], drug['l10n_preparation'])
    1352  #                       )
    1353  #                       return False
    1354
    1355          if parent is None:
    1356                  parent = wx.GetApp().GetTopWindow()
    1357
    1358  #       #--------------------------------------------
    1359  #       def manage_substances(drug):
    1360  #               manage_substance_doses(parent = parent)
    1361
    1362          #--------------------------------------------
    1363          ea = cSingleComponentGenericDrugEAPnl(parent, -1)
    1364          ea.data = drug
    1365          ea.mode = gmTools.coalesce(drug, 'new', 'edit')
    1366          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
    1367          if fields is not None:
    1368                  ea.set_fields(fields)
    1369          dlg.SetTitle(gmTools.coalesce(drug, _('Adding new single-component generic drug'), _('Editing single-component generic drug')))
    1370  #       dlg.left_extra_button = (
    1371  #               _('Substances'),
    1372  #               _('Manage substances'),
    1373  #               manage_substances
    1374  #       )
    1375          if dlg.ShowModal() == wx.ID_OK:
    1376                  drug = ea.data
    1377                  dlg.Destroy()
    1378                  if return_drug:
    1379                          return drug
    1380                  return True
    1381          dlg.Destroy()
    1382          if return_drug:
    1383                  return None
    1384          return False
    1385
    1386  #------------------------------------------------------------
    1387 -def manage_single_component_generic_drugs(parent=None, ignore_OK_button=False):
    1388
    1389          if parent is None:
    1390                  parent = wx.GetApp().GetTopWindow()
    1391
    1392          #------------------------------------------------------------
    1393          def add_from_db(drug):
    1394                  drug_db = get_drug_database(parent = parent)
    1395                  if drug_db is None:
    1396                          return False
    1397                  drug_db.import_drugs()
    1398                  return True
    1399
    1400          #------------------------------------------------------------
    1401          def get_tooltip(product=None):
    1402                  return product.format(include_component_details = True)
    1403
    1404          #------------------------------------------------------------
    1405          def edit(product):
    1406                  if product is not None:
    1407                          if product.is_vaccine:
    1408                                  gmGuiHelpers.gm_show_info (
    1409                                          aTitle = _('Editing medication'),
    1410                                          aMessage = _(
    1411                                                  'Cannot edit the medication\n'
    1412                                                  '\n'
    1413                                                  ' "%s" (%s)\n'
    1414                                                  '\n'
    1415                                                  'because it is a vaccine. Please edit it\n'
    1416                                                  'from the vaccine management section !\n'
    1417                                          ) % (product['product'], product['l10n_preparation'])
    1418                                  )
    1419                                  return False
    1420
    1421                  return edit_drug_product(parent = parent, drug_product = product, single_entry = True)
    1422
    1423          #------------------------------------------------------------
    1424          def delete(product):
    1425                  if product.is_vaccine:
    1426                          gmGuiHelpers.gm_show_info (
    1427                                  aTitle = _('Deleting medication'),
    1428                                  aMessage = _(
    1429                                          'Cannot delete the medication\n'
    1430                                          '\n'
    1431                                          ' "%s" (%s)\n'
    1432                                          '\n'
    1433                                          'because it is a vaccine. Please delete it\n'
    1434                                          'from the vaccine management section !\n'
    1435                                  ) % (product['product'], product['l10n_preparation'])
    1436                          )
    1437                          return False
    1438                  gmMedication.delete_drug_product(pk_drug_product = product['pk_drug_product'])
    1439                  return True
    1440
    1441          #------------------------------------------------------------
    1442          def new():
    1443                  return edit_drug_product(parent = parent, drug_product = None, single_entry = False)
    1444
    1445          #------------------------------------------------------------
    1446          def refresh(lctrl):
    1447                  drugs = gmMedication.get_drug_products()
    1448                  items = [ [
    1449                          '%s%s' % (
    1450                                  d['product'],
    1451                                  gmTools.bool2subst(d['is_fake_product'], ' (%s)' % _('fake'), '')
    1452                          ),
    1453                          d['l10n_preparation'],
    1454                          gmTools.coalesce(d['atc'], ''),
    1455                          '; '.join([ '%s %s%s' % (
    1456                                  c['substance'],
    1457                                  c['amount'],
    1458                                  gmMedication.format_units(c['unit'], c['dose_unit'])
    1459                          ) for c in d['components']]),
    1460                          gmTools.coalesce(d['external_code'], '', '%%s [%s]' % d['external_code_type']),
    1461                          d['pk_drug_product']
    1462                  ] for d in drugs ]
    1463                  lctrl.set_string_items(items)
    1464                  lctrl.set_data(drugs)
    1465
    1466          #------------------------------------------------------------
    1467          gmListWidgets.get_choices_from_list (
    1468                  parent = parent,
    1469                  caption = _('Drug products currently known to GNUmed.'),
    1470                  columns = [_('Name'), _('Preparation'), _('ATC'), _('Components'), _('Code'), '#'],
    1471                  single_selection = True,
    1472                  ignore_OK_button = ignore_OK_button,
    1473                  refresh_callback = refresh,
    1474                  new_callback = new,
    1475                  edit_callback = edit,
    1476                  delete_callback = delete,
    1477                  list_tooltip_callback = get_tooltip,
    1478                  #left_extra_button = (_('Import'), _('Import substances and products from a drug database.'), add_from_db)
    1479                  #, middle_extra_button = (_('Clone'), _('Clone selected drug into a new entry for editing.'), clone_from_existing)
    1480                  #, right_extra_button = (_('Reassign'), _('Reassign all patients taking the selected drug to another drug.'), reassign_patients)
    1481          )
    1482
    1483  #------------------------------------------------------------
    1484  from Gnumed.wxGladeWidgets import wxgSingleComponentGenericDrugEAPnl
    1485
    1486 -class cSingleComponentGenericDrugEAPnl(wxgSingleComponentGenericDrugEAPnl.wxgSingleComponentGenericDrugEAPnl, gmEditArea.cGenericEditAreaMixin):
    1487
    1488 -        def __init__(self, *args, **kwargs):
    1489
    1490                  try:
    1491                          data = kwargs['drug']
    1492                          del kwargs['drug']
    1493                  except KeyError:
    1494                          data = None
    1495
    1496                  wxgSingleComponentGenericDrugEAPnl.wxgSingleComponentGenericDrugEAPnl.__init__(self, *args, **kwargs)
    1497                  gmEditArea.cGenericEditAreaMixin.__init__(self)
    1498
    1499                  self.mode = 'new'
    1500                  self.data = data
    1501                  if data is not None:
    1502                          self.mode = 'edit'
    1503
    1504                  self.__init_ui()
    1505
    1506          #----------------------------------------------------------------
    1507 -        def __init_ui(self):
    1508                  self._PRW_substance.add_callback_on_modified(callback = self._on_name_field_modified)
    1509                  self._PRW_substance.add_callback_on_selection(callback = self._on_name_field_modified)
    1510                  self._TCTRL_amount.add_callback_on_modified(callback = self._on_name_field_modified)
    1511                  self._PRW_unit.add_callback_on_modified(callback = self._on_name_field_modified)
    1512                  self._PRW_unit.add_callback_on_selection(callback = self._on_name_field_modified)
    1513                  self._PRW_dose_unit.add_callback_on_modified(callback = self._on_name_field_modified)
    1514                  self._PRW_dose_unit.add_callback_on_selection(callback = self._on_name_field_modified)
    1515
    1516          #----------------------------------------------------------------
    1517          # generic Edit Area mixin API
    1518          #----------------------------------------------------------------
    1519 -        def _valid_for_save(self):
    1520
    1521                  validity = True
    1522
    1523                  if self._PRW_preparation.Value.strip() == '':
    1524                          validity = False
    1525                          self._PRW_preparation.display_as_valid(False)
    1526                          self.status_message = _('Drug form is missing.')
    1527                          self._PRW_preparation.SetFocus()
    1528                  else:
    1529                          self._PRW_preparation.display_as_valid(True)
    1530
    1531                  if self._PRW_unit.GetData() is None:
    1532                          validity = False
    1533                          self._PRW_unit.display_as_valid(False)
    1534                          self.status_message = _('Unit for amount is missing.')
    1535                          self._PRW_unit.SetFocus()
    1536                  else:
    1537                          self._PRW_unit.display_as_valid(True)
    1538
    1539                  if self._TCTRL_amount.GetValue().strip() == '':
    1540                          validity = False
    1541                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = False)
    1542                          self.status_message = _('Amount is missing.')
    1543                          self._TCTRL_amount.SetFocus()
    1544                  else:
    1545                          self.display_tctrl_as_valid(tctrl = self._TCTRL_amount, valid = True)
    1546
    1547                  if self._PRW_substance.GetData() is None:
    1548                          val = self._PRW_substance.Value.strip()
    1549                          if val != '' and gmATC.exists_as_atc(val):
    1550                                  subst = gmMedication.create_substance(substance = val)
    1551                                  self._PRW_substance.SetText(subst['substance'], subst['pk_substance'])
    1552                                  self._PRW_substance.display_as_valid(True)
    1553                          else:
    1554                                  validity = False
    1555                                  self._PRW_substance.display_as_valid(False)
    1556                                  self.status_message = _('Substance is missing.')
    1557                                  self._PRW_substance.SetFocus()
    1558                  else:
    1559                          self._PRW_substance.display_as_valid(True)
    1560
    1561                  return validity
    1562
    1563          #----------------------------------------------------------------
    1564 -        def _save_as_new(self):
    1565
    1566                  dose = gmMedication.create_substance_dose (
    1567                          pk_substance = self._PRW_substance.GetData(),
    1568                          amount = self._TCTRL_amount.Value.strip(),
    1569                          unit = self._PRW_unit.GetData(),
    1570                          dose_unit = self._PRW_dose_unit.GetData()
    1571                  )
    1572                  dose_unit = self._PRW_dose_unit.GetValue().strip()
    1573                  if dose_unit != '':
    1574                          dose_unit = '/' + dose_unit
    1575                  name = '%s %s%s%s' % (
    1576                          self._PRW_substance.GetValue().strip(),
    1577                          self._TCTRL_amount.Value.strip(),
    1578                          self._PRW_unit.GetValue().strip(),
    1579                          dose_unit
    1580                  )
    1581                  drug = gmMedication.create_drug_product (
    1582                          product_name = name,
    1583                          preparation = self._PRW_preparation.GetValue().strip(),
    1584                          return_existing = True
    1585                  )
    1586                  drug['is_fake_product'] = True
    1587                  drug.save()
    1588                  drug.set_substance_doses_as_components(substance_doses = [dose])
    1589                  self.data = drug
    1590                  return True
    1591
    1592          #----------------------------------------------------------------
    1593 -        def _save_as_update(self):
    1594                  return False
    1595  #               self.data[''] = self._CHBOX_xxx.GetValue()
    1596  #               self.data.save()
    1597  #               return True
    1598
    1599          #----------------------------------------------------------------
    1600 -        def _refresh_as_new(self):
    1601                  self._LBL_drug_name.SetLabel('')
    1602                  self._PRW_substance.SetText('', None)
    1603                  self._TCTRL_amount.SetValue('')
    1604                  self._PRW_unit.SetText('', None)
    1605                  self._PRW_dose_unit.SetText('', None)
    1606                  self._PRW_preparation.SetText('', None)
    1607
    1608          #----------------------------------------------------------------
    1609 -        def _refresh_as_new_from_existing(self):
    1610                  self._refresh_as_new()
    1611
    1612          #----------------------------------------------------------------
    1613 -        def _refresh_from_existing(self):
    1614                  pass
    1615
    1616          #----------------------------------------------------------------
    1617 -        def set_fields(self, fields):
    1618                  try:
    1619                          self._PRW_substance.SetText(fields['substance']['value'], fields['substance']['data'])
    1620                  except KeyError:
    1621                          _log.error('cannot set field [substance] from <%s>', fields)
    1622
    1623          #----------------------------------------------------------------
    1624 -        def _on_name_field_modified(self, data=None):
    1625                  dose_unit = self._PRW_dose_unit.GetValue().strip()
    1626                  if dose_unit != '':
    1627                          dose_unit = '/' + dose_unit
    1628                  name = '%s %s%s%s' % (
    1629                          self._PRW_substance.GetValue().strip(),
    1630                          self._TCTRL_amount.Value.strip(),
    1631                          self._PRW_unit.GetValue().strip(),
    1632                          dose_unit
    1633                  )
    1634                  self._LBL_drug_name.SetLabel('"%s"' % name.strip())
    1635
    1636  #============================================================
    1637  # main
    1638  #------------------------------------------------------------
    1639  if __name__ == '__main__':
    1640
    1641          if len(sys.argv) < 2:
    1642                  sys.exit()
    1643
    1644          if sys.argv[1] != 'test':
    1645                  sys.exit()
    1646
    1647          from Gnumed.business import gmPersonSearch
    1648
    1649          pat = gmPersonSearch.ask_for_patient()
    1650          if pat is None:
    1651                  sys.exit()
    1652          gmPerson.set_active_patient(patient = pat)
    1653
    1654          #----------------------------------------
    1655          app = wx.PyWidgetTester(size = (600, 300))
    1656          app.SetWidget(cSubstancePhraseWheel, -1)
    1657          app.MainLoop()
    1658          #manage_substance_intakes()
    1659          edit_single_component_generic_drug (
    1660                  single_entry = True,
    1661                  fields = {'substance': {'value': val, 'data': None}},
    1662                  return_drug = True
    1663          )
    1664