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