Gnumed.wxpython.gmMedicationWidgets

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

# Source Code for Module Gnumed.wxpython.gmMedicationWidgets

       1  """GNUmed medication handling widgets."""
       2
       3  #================================================================
       4  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
       5  __license__ = "GPL v2 or later"
       6
       7  import logging
       8  import sys
       9  import datetime as pydt
      10
      11
      12  import wx
      13  import wx.grid
      14
      15
      16  if __name__ == '__main__':
      17          sys.path.insert(0, '../../')
      18          from Gnumed.pycommon import gmI18N
      19          gmI18N.activate_locale()
      20          gmI18N.install_domain(domain = 'gnumed')
      21
      22  from Gnumed.pycommon import gmDispatcher
      23  from Gnumed.pycommon import gmCfg
      24  from Gnumed.pycommon import gmTools
      25  from Gnumed.pycommon import gmDateTime
      26  from Gnumed.pycommon import gmMatchProvider
      27  from Gnumed.pycommon import gmI18N
      28  from Gnumed.pycommon import gmPrinting
      29  from Gnumed.pycommon import gmCfg2
      30  from Gnumed.pycommon import gmNetworkTools
      31
      32  from Gnumed.business import gmPerson
      33  from Gnumed.business import gmPraxis
      34  from Gnumed.business import gmMedication
      35  from Gnumed.business import gmForms
      36  from Gnumed.business import gmStaff
      37  from Gnumed.business import gmDocuments
      38  from Gnumed.business import gmLOINC
      39  from Gnumed.business import gmClinicalRecord
      40  from Gnumed.business import gmClinicalCalculator
      41  from Gnumed.business import gmPathLab
      42
      43  from Gnumed.wxpython import gmGuiHelpers
      44  from Gnumed.wxpython import gmRegetMixin
      45  from Gnumed.wxpython import gmAuthWidgets
      46  from Gnumed.wxpython import gmEditArea
      47  from Gnumed.wxpython import gmMacro
      48  from Gnumed.wxpython import gmCfgWidgets
      49  from Gnumed.wxpython import gmListWidgets
      50  from Gnumed.wxpython import gmPhraseWheel
      51  from Gnumed.wxpython import gmFormWidgets
      52  from Gnumed.wxpython import gmAllergyWidgets
      53  from Gnumed.wxpython import gmDocumentWidgets
      54  from Gnumed.wxpython import gmSubstanceMgmtWidgets
      55
      56
      57  _log = logging.getLogger('gm.ui')
      58
      59  #============================================================
      60  # perhaps leave this here:
      61  #============================================================
      62 -class cSubstancePreparationPhraseWheel(gmPhraseWheel.cPhraseWheel):
      63
      64 -        def __init__(self, *args, **kwargs):
      65
      66                  query = """
      67  SELECT DISTINCT ON (list_label)
      68          preparation AS data,
      69          preparation AS list_label,
      70          preparation AS field_label
      71  FROM ref.drug_product
      72  WHERE preparation %(fragment_condition)s
      73  ORDER BY list_label
      74  LIMIT 30"""
      75                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
      76                  mp.setThresholds(1, 2, 4)
      77                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
      78                  self.SetToolTip(_('The preparation (form) of the substance or product.'))
      79                  self.matcher = mp
      80                  self.selection_only = False
      81
      82  #============================================================
      83  # current substance intake widgets
      84  #------------------------------------------------------------
      85 -class cSubstanceIntakeObjectPhraseWheel(gmPhraseWheel.cPhraseWheel):
      86
      87 -        def __init__(self, *args, **kwargs):
      88                  mp = gmMedication.cSubstanceIntakeObjectMatchProvider()
      89                  mp.setThresholds(1, 2, 4)
      90                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
      91                  self.SetToolTip(_('A drug product.'))
      92                  self.matcher = mp
      93                  self.selection_only = True
      94                  self.phrase_separators = None
      95
      96          #--------------------------------------------------------
      97 -        def _data2instance(self):
      98                  pk = self.GetData(as_instance = False, can_create = False)
      99                  if pk is None:
     100                          return None
     101                  return gmMedication.cDrugProduct(aPK_obj = pk)
     102
     103  #------------------------------------------------------------
     104 -class cProductOrSubstancePhraseWheel(gmPhraseWheel.cPhraseWheel):
     105
     106 -        def __init__(self, *args, **kwargs):
     107
     108                  mp = gmMedication.cProductOrSubstanceMatchProvider()
     109                  mp.setThresholds(1, 2, 4)
     110                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     111                  self.SetToolTip(_('A substance with optional strength or a drug product.'))
     112                  self.matcher = mp
     113                  self.selection_only = False
     114                  self.phrase_separators = None
     115                  self.IS_PRODUCT = 1
     116                  self.IS_SUBSTANCE = 2
     117                  self.IS_COMPONENT = 3
     118
     119          #--------------------------------------------------------
     120 -        def _data2instance(self):
     121                  entry_type, pk = self.GetData(as_instance = False, can_create = False)
     122                  if entry_type == 1:
     123                          return gmMedication.cDrugProduct(aPK_obj = pk)
     124                  if entry_type == 2:
     125                          return gmMedication.cConsumableSubstance(aPK_obj = pk)
     126                  if entry_type == 3:
     127                          return gmMedication.cDrugComponent(aPK_obj = pk)
     128                  raise ValueError('entry type must be 1=drug product or 2=substance or 3=component')
     129
     130  #============================================================
     131 -class cSubstanceSchedulePhraseWheel(gmPhraseWheel.cPhraseWheel):
     132
     133 -        def __init__(self, *args, **kwargs):
     134
     135                  query = """
     136                          SELECT DISTINCT ON (sched)
     137                                  schedule as sched,
     138                                  schedule
     139                          FROM clin.substance_intake
     140                          WHERE schedule %(fragment_condition)s
     141                          ORDER BY sched
     142                          LIMIT 50"""
     143
     144                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
     145                  mp.setThresholds(1, 2, 4)
     146                  mp.word_separators = '[ \t=+&:@]+'
     147                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     148                  self.SetToolTip(_('The schedule for taking this substance.'))
     149                  self.matcher = mp
     150                  self.selection_only = False
     151
     152  #============================================================
     153 -class cSubstanceAimPhraseWheel(gmPhraseWheel.cPhraseWheel):
     154
     155 -        def __init__(self, *args, **kwargs):
     156
     157                  query = """
     158  (
     159          SELECT DISTINCT ON (field_label)
     160                  aim
     161                          AS data,
     162                  aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
     163                          AS list_label,
     164                  aim
     165                          AS field_label
     166          FROM clin.v_substance_intakes
     167          WHERE
     168                  aim %(fragment_condition)s
     169                  %(ctxt_substance)s
     170  ) UNION (
     171          SELECT DISTINCT ON (field_label)
     172                  aim
     173                          AS data,
     174                  aim || ' (' || substance || ' ' || amount || ' ' || unit || ')'
     175                          AS list_label,
     176                  aim
     177                          AS field_label
     178          FROM clin.v_substance_intakes
     179          WHERE
     180                  aim %(fragment_condition)s
     181  )
     182  ORDER BY list_label
     183  LIMIT 30"""
     184
     185                  context = {'ctxt_substance': {
     186                          'where_part': 'AND substance = %(substance)s',
     187                          'placeholder': 'substance'
     188                  }}
     189
     190                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
     191                  mp.setThresholds(1, 2, 4)
     192                  #mp.word_separators = '[ \t=+&:@]+'
     193                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     194                  self.SetToolTip(_('The medical aim for consuming this substance.'))
     195                  self.matcher = mp
     196                  self.selection_only = False
     197
     198  #============================================================
     199 -def turn_substance_intake_into_allergy(parent=None, intake=None, emr=None):
     200
     201          if intake['is_currently_active']:
     202                  intake['discontinued'] = gmDateTime.pydt_now_here()
     203          if intake['discontinue_reason'] is None:
     204                  intake['discontinue_reason'] = '%s %s' % (_('not tolerated:'), _('discontinued due to allergy or intolerance'))
     205          else:
     206                  if not intake['discontinue_reason'].startswith(_('not tolerated:')):
     207                          intake['discontinue_reason'] = '%s %s' % (_('not tolerated:'), intake['discontinue_reason'])
     208          if not intake.save():
     209                  return False
     210
     211          allg = intake.turn_into_allergy(encounter_id = emr.active_encounter['pk_encounter'])
     212
     213          drug = intake.containing_drug
     214          comps = [ c['substance'] for c in drug.components ]
     215          if len(comps) > 1:
     216                  gmGuiHelpers.gm_show_info (
     217                          aTitle = _('Documented an allergy'),
     218                          aMessage = _(
     219                                  'An allergy was documented against the substance:\n'
     220                                  '\n'
     221                                  '  [%s]\n'
     222                                  '\n'
     223                                  'This substance was taken with the multi-component drug product:\n'
     224                                  '\n'
     225                                  '  [%s (%s)]\n'
     226                                  '\n'
     227                                  'Note that ALL components of this product were discontinued.'
     228                          ) % (
     229                                  intake['substance'],
     230                                  intake['product'],
     231                                  ' & '.join(comps)
     232                          )
     233                  )
     234
     235          if parent is None:
     236                  parent = wx.GetApp().GetTopWindow()
     237
     238          dlg = gmAllergyWidgets.cAllergyManagerDlg(parent, -1)
     239          dlg.ShowModal()
     240
     241          return True
     242
     243  #============================================================
     244 -def manage_substance_intakes(parent=None, emr=None):
     245
     246          if parent is None:
     247                  parent = wx.GetApp().GetTopWindow()
     248
     249          if emr is None:
     250                  emr = gmPerson.gmCurrentPatient().emr
     251  #       #------------------------------------------------------------
     252  #       def add_from_db(substance):
     253  #               drug_db = gmSubstanceMgmtWidgets.get_drug_database(parent = parent, patient = gmPerson.gmCurrentPatient())
     254  #               if drug_db is None:
     255  #                       return False
     256  #               drug_db.import_drugs()
     257  #               return True
     258  #       #------------------------------------------------------------
     259  #       def edit(substance=None):
     260  #               return gmSubstanceMgmtWidgets.edit_substance(parent = parent, substance = substance, single_entry = (substance is not None))
     261  #       #------------------------------------------------------------
     262  #       def delete(substance):
     263  #               if substance.is_in_use_by_patients:
     264  #                       gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this substance. It is in use.'), beep = True)
     265  #                       return False
     266  #
     267  #               xxxxx -> substance_dose
     268  #               return gmMedication.delete_xsubstance(substance = substance['pk'])
     269          #------------------------------------------------------------
     270          def get_tooltip(intake=None):
     271                  return intake.format(single_line = False, show_all_product_components = True)
     272          #------------------------------------------------------------
     273          def refresh(lctrl):
     274                  intakes = emr.get_current_medications (
     275                          include_inactive = False,
     276                          include_unapproved = True,
     277                          order_by = 'substance, product, started'
     278                  )
     279                  items = []
     280                  for i in intakes:
     281                          started = i.medically_formatted_start
     282                          items.append ([
     283                                  '%s%s %s %s %s%s' % (
     284                                          i['substance'],
     285                                          gmTools.coalesce(i['product'], '', ' (%s)'),
     286                                          i['amount'],
     287                                          i['unit'],
     288                                          i['l10n_preparation'],
     289                                          gmTools.coalesce(i['external_code_product'], '', ' [%s::%s]' % (i['external_code_type_product'], i['external_code_product']))
     290                                  ),
     291                                  '%s%s%s' % (
     292                                          started,
     293                                          gmTools.coalesce(i['schedule'], '', ' %%s %s' % gmTools.u_arrow2right),
     294                                          gmTools.coalesce(i['duration'], '', ' %s')
     295                                  ),
     296                                  '%s' % (
     297                                          gmTools.bool2subst (
     298                                                  i['intake_is_approved_of'],
     299                                                  '',
     300                                                  _('disapproved')
     301                                          )
     302                                  )
     303                          ])
     304                  lctrl.set_string_items(items)
     305                  lctrl.set_data(intakes)
     306
     307          #------------------------------------------------------------
     308          return gmListWidgets.get_choices_from_list (
     309                  parent = parent,
     310                  caption = _('Substances consumed by the patient'),
     311                  columns = [ _('Intake'), _('Application'), _('Status') ],
     312                  single_selection = False,
     313  #               new_callback = edit,
     314  #               edit_callback = edit,
     315  #               delete_callback = delete,
     316                  refresh_callback = refresh,
     317                  list_tooltip_callback = get_tooltip
     318  #               ,left_extra_button = (_('Import'), _('Import consumable substances from a drug database.'), add_from_db)
     319          )
     320
     321  #============================================================
     322  from Gnumed.wxGladeWidgets import wxgCurrentMedicationEAPnl
     323
     324 -class cSubstanceIntakeEAPnl(wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl, gmEditArea.cGenericEditAreaMixin):
     325
     326 -        def __init__(self, *args, **kwargs):
     327
     328                  try:
     329                          data = kwargs['substance']
     330                          del kwargs['substance']
     331                  except KeyError:
     332                          data = None
     333
     334                  self.calc = gmClinicalCalculator.cClinicalCalculator()
     335
     336                  wxgCurrentMedicationEAPnl.wxgCurrentMedicationEAPnl.__init__(self, *args, **kwargs)
     337                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     338
     339                  self.mode = 'new'
     340                  self.data = data
     341                  if data is not None:
     342                          self.mode = 'edit'
     343
     344                  self.__init_ui()
     345
     346          #----------------------------------------------------------------
     347 -        def __init_ui(self):
     348
     349                  self._PRW_drug.add_callback_on_lose_focus(callback = self._on_leave_drug)
     350                  self._PRW_drug.selection_only = True
     351
     352                  self._PRW_duration.display_accuracy = gmDateTime.acc_days
     353
     354                  # this we want to adjust later
     355                  self._PRW_aim.add_callback_on_set_focus(callback = self._on_enter_aim)
     356
     357                  self._DP_discontinued.add_callback_on_selection(callback = self._on_discontinued_date_changed)
     358
     359          #----------------------------------------------------------------
     360 -        def __refresh_precautions(self):
     361
     362                  curr_pat = gmPerson.gmCurrentPatient()
     363                  emr = curr_pat.emr
     364
     365                  # allergies
     366                  state = emr.allergy_state
     367                  if state['last_confirmed'] is None:
     368                          confirmed = _('never')
     369                  else:
     370                          confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
     371                  msg = _('%s, last confirmed %s\n') % (state.state_string, confirmed)
     372                  msg += gmTools.coalesce(state['comment'], '', _('Comment (%s): %%s\n') % state['modified_by'])
     373                  tooltip = ''
     374                  allgs = emr.get_allergies()
     375                  if len(allgs) > 0:
     376                          msg += '\n'
     377                  for allergy in allgs:
     378                          msg += '%s: %s (%s)\n' % (
     379                                  allergy['descriptor'],
     380                                  allergy['l10n_type'],
     381                                  gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'), '?')
     382                          )
     383                          tooltip += '%s: %s\n' % (
     384                                  allergy['descriptor'],
     385                                  gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
     386                          )
     387                  if len(allgs) > 0:
     388                          msg += '\n'
     389                          tooltip += '\n'
     390                  del allgs
     391
     392                  # history of substance abuse
     393                  abuses = emr.abused_substances
     394                  for abuse in abuses:
     395                          tooltip += abuse.format(single_line = False, include_metadata = False)
     396                          tooltip += '\n'
     397                          if abuse['harmful_use_type'] in [None, 0]:
     398                                  continue
     399                          msg += abuse.format(single_line = True)
     400                          msg += '\n'
     401                  if len(abuses) > 0:
     402                          msg += '\n'
     403                          tooltip += '\n'
     404                  del abuses
     405
     406                  # kidney function
     407                  gfrs = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
     408                  if len(gfrs) == 0:
     409                          self.calc.patient = curr_pat
     410                          gfr = self.calc.eGFR
     411                          if gfr.numeric_value is None:
     412                                  msg += _('GFR: unknown')
     413                          else:
     414                                  msg += gfr.message
     415                                  egfrs = self.calc.eGFRs
     416                                  tts = []
     417                                  for egfr in egfrs:
     418                                          if egfr.numeric_value is None:
     419                                                  continue
     420                                          tts.append(egfr.format (
     421                                                  left_margin = 0,
     422                                                  width = 50,
     423                                                  eol = '\n',
     424                                                  with_formula = False,
     425                                                  with_warnings = True,
     426                                                  with_variables = False,
     427                                                  with_sub_results = False,
     428                                                  return_list = False
     429                                          ))
     430                                  tooltip += '\n'.join(tts)
     431                  else:
     432                          gfr = gfrs[0]
     433                          msg += '%s: %s %s (%s)\n' % (
     434                                  gfr['unified_abbrev'],
     435                                  gfr['unified_val'],
     436                                  gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
     437                                  gmDateTime.pydt_strftime (
     438                                          gfr['clin_when'],
     439                                          format = '%Y %b %d'
     440                                  )
     441                          )
     442                          tooltip += _('GFR reported by path lab')
     443
     444                  # pregnancy
     445                  edc = emr.EDC
     446                  if edc is not None:
     447                          msg += '\n\n'
     448                          if emr.EDC_is_fishy:
     449                                  msg += _('EDC (!?!): %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
     450                                  tooltip += _(
     451                                          'The Expected Date of Confinement is rather questionable.\n'
     452                                          '\n'
     453                                          'Please check patient age, patient gender, time until/since EDC.'
     454                                  )
     455                          else:
     456                                  msg += _('EDC: %s') % gmDateTime.pydt_strftime(edc, format = '%Y %b %d')
     457
     458                  self._LBL_allergies.SetLabel(msg)
     459                  self._LBL_allergies.SetToolTip(tooltip)
     460
     461          #----------------------------------------------------------------
     462 -        def __refresh_drug_details(self):
     463
     464                  drug = self._PRW_drug.GetData(as_instance = True)
     465                  if drug is None:
     466                          self._LBL_drug_details.SetLabel('')
     467                          self._LBL_drug_details.SetToolTip('')
     468                          self.Layout()
     469                          return
     470
     471                  if len(drug['components']) == 0:
     472                          comps = _('<no components>')
     473                  else:
     474                          comps = '\n'.join ([
     475                                  ' %s %s%s%s' % (
     476                                          c['substance'],
     477                                          c['amount'],
     478                                          c['unit'],
     479                                          gmTools.coalesce(c['dose_unit'], '', '/%s')
     480                                  )
     481                                  for c in drug['components']
     482                          ])
     483                  self._LBL_drug_details.SetLabel('%s\n%s' % (drug['product'], comps))
     484                  self._LBL_drug_details.SetToolTip(drug.format())
     485                  self.Layout()
     486                  return
     487
     488          #----------------------------------------------------------------
     489          # generic Edit Area mixin API
     490          #----------------------------------------------------------------
     491 -        def _check_drug_is_valid(self):
     492
     493                  self._PRW_drug.display_as_valid(True)
     494
     495                  # if we are editing the drug SHOULD exist so don't error
     496                  if self.mode == 'edit':
     497                          return True
     498
     499                  selected_drug = self._PRW_drug.GetData(as_instance = True)
     500
     501                  # no drug selected
     502                  if selected_drug is None:
     503                          val = self._PRW_drug.GetValue().strip()
     504                          if val == '':
     505                                  self._PRW_drug.display_as_valid(False)
     506                                  self._PRW_drug.SetFocus()
     507                                  return False
     508                          # create as a generic, single-substance drug if that does not exist
     509                          drug = gmSubstanceMgmtWidgets.edit_single_component_generic_drug (
     510                                  parent = self,
     511                                  drug = None,
     512                                  single_entry = True,
     513                                  fields = {'substance': {'value': val, 'data': None}},
     514                                  return_drug = True
     515                          )
     516                          if drug is None:
     517                                  self._PRW_drug.display_as_valid(False)
     518                                  self._PRW_drug.SetFocus()
     519                                  return False
     520                          comp = drug.components[0]
     521                          self._PRW_drug.SetText (
     522                                  _('%s w/ %s%s%s of %s') % (
     523                                          comp['product'],
     524                                          comp['amount'],
     525                                          comp['unit'],
     526                                          gmTools.coalesce(comp['dose_unit'], '', '/%s'),
     527                                          comp['substance']
     528                                  ),
     529                                  drug['pk_drug_product']
     530                          )
     531                          selected_drug = drug
     532                          self.__refresh_drug_details()
     533                          self._PRW_drug.display_as_valid(True)
     534                          self._PRW_drug.SetFocus()
     535                          # return False despite there's now a drug such
     536                          # that the user has another chance to inspect
     537                          # the edit area data after creating a new drug
     538                          return False
     539
     540                  # drug already exists as intake
     541                  if selected_drug.exists_as_intake(pk_patient = gmPerson.gmCurrentPatient().ID):
     542                          title = _('Adding substance intake entry')
     543                          msg = _(
     544                                  'The patient is already taking\n'
     545                                  '\n'
     546                                  ' %s\n'
     547                                  '\n'
     548                                  'You will want to adjust the schedule\n'
     549                                  'rather than document the intake twice.'
     550                          ) % self._PRW_drug.GetValue().strip()
     551                          gmGuiHelpers.gm_show_warning(aTitle = title, aMessage = msg)
     552                          self._PRW_drug.display_as_valid(False)
     553                          self._PRW_drug.SetFocus()
     554                          return False
     555
     556                  self._PRW_drug.display_as_valid(True)
     557                  return True
     558
     559          #----------------------------------------------------------------
     560 -        def _valid_for_save(self):
     561
     562                  validity = self._check_drug_is_valid()
     563
     564                  # episode must be set if intake is to be approved of
     565                  if self._CHBOX_approved.IsChecked():
     566                          if self._PRW_episode.GetValue().strip() == '':
     567                                  self._PRW_episode.display_as_valid(False)
     568                                  validity = False
     569                          else:
     570                                  self._PRW_episode.display_as_valid(True)
     571
     572                  if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
     573                          self._PRW_duration.display_as_valid(True)
     574                  else:
     575                          if self._PRW_duration.GetData() is None:
     576                                  # no data ...
     577                                  if gmDateTime.str2interval(self._PRW_duration.GetValue()) is None:
     578                                          self._PRW_duration.display_as_valid(False)
     579                                          validity = False
     580                                  # ... but valid string
     581                                  else:
     582                                          self._PRW_duration.display_as_valid(True)
     583                          # has data
     584                          else:
     585                                  self._PRW_duration.display_as_valid(True)
     586
     587                  # started must exist or be unknown
     588                  started = None
     589                  if self._CHBOX_start_unknown.IsChecked() is False:
     590                          started = self._DP_started.GetData()
     591                          if started is None:
     592                                  self._DP_started.display_as_valid(False)
     593                                  self._DP_started.SetFocus()
     594                                  validity = False
     595                          else:
     596                                  self._DP_started.display_as_valid(True)
     597
     598                  if validity is False:
     599                          self.StatusText = _('Input incomplete/invalid for saving as substance intake.')
     600
     601                  # discontinued must be "< now()" AND "> started" if at all
     602                  discontinued = self._DP_discontinued.GetData()
     603                  if discontinued is not None:
     604                          now = gmDateTime.pydt_now_here().replace (
     605                                  hour = 23,
     606                                  minute = 59,
     607                                  second = 59,
     608                                  microsecond = 111111
     609                          )
     610                          # not in the future
     611                          if discontinued > now:
     612                                  self._DP_discontinued.display_as_valid(False)
     613                                  validity = False
     614                                  self.StatusText = _('Discontinued (%s) in the future (now: %s)!') % (discontinued, now)
     615                          else:
     616                                  if started is not None:
     617                                          started = started.replace (
     618                                                  hour = 0,
     619                                                  minute = 0,
     620                                                  second = 0,
     621                                                  microsecond = 1
     622                                          )
     623                                          # and not before it was started
     624                                          if started > discontinued:
     625                                                  self._DP_started.display_as_valid(False)
     626                                                  self._DP_discontinued.display_as_valid(False)
     627                                                  validity = False
     628                                                  self.StatusText = _('Discontinued (%s) before started (%s) !') % (discontinued, started)
     629                                          else:
     630                                                  self._DP_started.display_as_valid(True)
     631                                                  self._DP_discontinued.display_as_valid(True)
     632
     633                  return validity
     634
     635          #----------------------------------------------------------------
     636 -        def _save_as_new(self):
     637
     638                  epi = self._PRW_episode.GetData()
     639                  if epi is None:
     640                          # create new episode, Jim wants it to auto-open
     641                          epi = self._PRW_episode.GetData(can_create = True, is_open = True)
     642
     643                  selected_drug = self._PRW_drug.GetData(as_instance = True)
     644                  intake = selected_drug.turn_into_intake (
     645                          encounter = gmPerson.gmCurrentPatient().emr.current_encounter['pk_encounter'],
     646                          episode = epi
     647                  )
     648
     649                  if intake is None:
     650                          self.StatusText = _('Cannot add duplicate of (maybe inactive) substance intake.')
     651                          return False
     652
     653                  intake['started'] = self._DP_started.GetData()
     654                  if self._CHBOX_start_unknown.IsChecked():
     655                          intake['comment_on_start'] = '?'
     656                  else:
     657                          intake['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
     658                  intake['discontinued'] = self._DP_discontinued.GetData()
     659                  if intake['discontinued'] is None:
     660                          intake['discontinue_reason'] = None
     661                  else:
     662                          intake['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
     663                  intake['schedule'] = self._PRW_schedule.GetValue().strip()
     664                  intake['aim'] = self._PRW_aim.GetValue().strip()
     665                  intake['notes'] = self._PRW_notes.GetValue().strip()
     666                  intake['is_long_term'] = self._CHBOX_long_term.IsChecked()
     667                  intake['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
     668                  if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
     669                          intake['duration'] = None
     670                  else:
     671                          if self._PRW_duration.GetData() is None:
     672                                  intake['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
     673                          else:
     674                                  intake['duration'] = self._PRW_duration.GetData()
     675                  intake.save()
     676
     677                  self.data = intake
     678
     679                  return True
     680
     681          #----------------------------------------------------------------
     682 -        def _save_as_update(self):
     683
     684                  # auto-applies to all components of a multi-component drug if any:
     685                  self.data['started'] = self._DP_started.GetData()
     686                  if self._CHBOX_start_unknown.IsChecked():
     687                          self.data['comment_on_start'] = '?'
     688                  else:
     689                          self.data['comment_on_start'] = self._PRW_start_certainty.GetValue().strip()
     690                  self.data['discontinued'] = self._DP_discontinued.GetData()
     691                  if self.data['discontinued'] is None:
     692                          self.data['discontinue_reason'] = None
     693                  else:
     694                          self.data['discontinue_reason'] = self._PRW_discontinue_reason.GetValue().strip()
     695                  self.data['schedule'] = self._PRW_schedule.GetValue()
     696                  self.data['is_long_term'] = self._CHBOX_long_term.IsChecked()
     697                  self.data['intake_is_approved_of'] = self._CHBOX_approved.IsChecked()
     698                  if self._PRW_duration.GetValue().strip() in ['', gmTools.u_infinity]:
     699                          self.data['duration'] = None
     700                  else:
     701                          if self._PRW_duration.GetData() is None:
     702                                  self.data['duration'] = gmDateTime.str2interval(self._PRW_duration.GetValue())
     703                          else:
     704                                  self.data['duration'] = self._PRW_duration.GetData()
     705
     706                  # per-component
     707                  self.data['aim'] = self._PRW_aim.GetValue()
     708                  self.data['notes'] = self._PRW_notes.GetValue()
     709                  epi = self._PRW_episode.GetData()
     710                  if epi is None:
     711                          # create new episode, Jim wants it to auto-open
     712                          epi = self._PRW_episode.GetData(can_create = True, is_open = True)
     713                  self.data['pk_episode'] = epi
     714
     715                  self.data.save()
     716
     717                  return True
     718
     719          #----------------------------------------------------------------
     720 -        def _refresh_as_new(self):
     721                  self._PRW_drug.SetText('', None)
     722
     723                  self._PRW_schedule.SetText('', None)
     724                  self._PRW_duration.SetText('', None)
     725                  self._PRW_aim.SetText('', None)
     726                  self._PRW_notes.SetText('', None)
     727                  self._PRW_episode.SetText('', None)
     728
     729                  self._CHBOX_long_term.SetValue(False)
     730                  self._CHBOX_approved.SetValue(True)
     731
     732                  self._CHBOX_start_unknown.SetValue(False)
     733                  self._DP_started.SetData(gmDateTime.pydt_now_here())
     734                  self._DP_started.Enable(True)
     735                  self._PRW_start_certainty.SetText('', None)
     736                  self._PRW_start_certainty.Enable(True)
     737                  self._DP_discontinued.SetData(None)
     738                  self._PRW_discontinue_reason.SetValue('')
     739                  self._PRW_discontinue_reason.Enable(False)
     740
     741                  self.__refresh_drug_details()
     742                  self.__refresh_precautions()
     743
     744                  self._PRW_drug.SetFocus()
     745
     746          #----------------------------------------------------------------
     747 -        def _refresh_from_existing(self):
     748
     749                  self._PRW_drug.SetText (
     750                          _('%s w/ %s%s%s of %s') % (
     751                                  self.data['product'],
     752                                  self.data['amount'],
     753                                  self.data['unit'],
     754                                  gmTools.coalesce(self.data['dose_unit'], '', '/%s'),
     755                                  self.data['substance']
     756                          ),
     757                          self.data['pk_drug_product']
     758                  )
     759
     760                  self._PRW_drug.Disable()
     761
     762                  if self.data['is_long_term']:
     763                          self._CHBOX_long_term.SetValue(True)
     764                          self._PRW_duration.Enable(False)
     765                          self._PRW_duration.SetText(gmTools.u_infinity, None)
     766                          self._BTN_discontinued_as_planned.Enable(False)
     767                  else:
     768                          self._CHBOX_long_term.SetValue(False)
     769                          self._PRW_duration.Enable(True)
     770                          self._BTN_discontinued_as_planned.Enable(True)
     771                          self._PRW_duration.SetData(self.data['duration'])
     772                  self._PRW_aim.SetText(gmTools.coalesce(self.data['aim'], ''), self.data['aim'])
     773                  self._PRW_notes.SetText(gmTools.coalesce(self.data['notes'], ''), self.data['notes'])
     774                  self._PRW_episode.SetData(self.data['pk_episode'])
     775                  self._PRW_schedule.SetText(gmTools.coalesce(self.data['schedule'], ''), self.data['schedule'])
     776
     777                  self._CHBOX_approved.SetValue(self.data['intake_is_approved_of'])
     778
     779                  self._DP_started.SetData(self.data['started'])
     780                  self._PRW_start_certainty.SetText(self.data['comment_on_start'], None)
     781                  if self.data['start_is_unknown']:
     782                          self._CHBOX_start_unknown.SetValue(True)
     783                          self._DP_started.Enable(False)
     784                          self._PRW_start_certainty.Enable(False)
     785                  else:
     786                          self._CHBOX_start_unknown.SetValue(False)
     787                          self._DP_started.Enable(True)
     788                          self._PRW_start_certainty.Enable(True)
     789
     790                  self._DP_discontinued.SetData(self.data['discontinued'])
     791                  self._PRW_discontinue_reason.SetValue(gmTools.coalesce(self.data['discontinue_reason'], ''))
     792                  if self.data['discontinued'] is not None:
     793                          self._PRW_discontinue_reason.Enable()
     794
     795                  self.__refresh_drug_details()
     796                  self.__refresh_precautions()
     797
     798                  self._PRW_schedule.SetFocus()
     799
     800          #----------------------------------------------------------------
     801 -        def _refresh_as_new_from_existing(self):
     802                  self._refresh_as_new()
     803
     804                  self._PRW_episode.SetData(self.data['pk_episode'])
     805                  self._DP_started.SetData(self.data['started'])
     806
     807                  self._PRW_drug.SetFocus()
     808
     809          #----------------------------------------------------------------
     810          # event handlers
     811          #----------------------------------------------------------------
     812 -        def _on_leave_drug(self):
     813                  self.__refresh_drug_details()
     814
     815          #----------------------------------------------------------------
     816 -        def _on_enter_aim(self):
     817                  drug = self._PRW_drug.GetData(as_instance = True)
     818                  if drug is None:
     819                          self._PRW_aim.unset_context(context = 'substance')
     820                          return
     821                  # do not set to self._PRW_drug.GetValue() as that will contain all
     822                  # sorts of additional info, rather set to the canonical drug['substance']
     823  #               self._PRW_aim.set_context(context = u'substance', val = drug['substance'])
     824
     825          #----------------------------------------------------------------
     826 -        def _on_discontinued_date_changed(self, event):
     827                  if self._DP_discontinued.GetData() is None:
     828                          self._PRW_discontinue_reason.Enable(False)
     829                  else:
     830                          self._PRW_discontinue_reason.Enable(True)
     831
     832          #----------------------------------------------------------------
     833 -        def _on_manage_components_button_pressed(self, event):
     834                  gmSubstanceMgmtWidgets.manage_drug_components()
     835
     836          #----------------------------------------------------------------
     837 -        def _on_manage_substances_button_pressed(self, event):
     838                  gmSubstanceMgmtWidgets.manage_substances()
     839
     840          #----------------------------------------------------------------
     841 -        def _on_manage_drug_products_button_pressed(self, event):
     842                  gmSubstanceMgmtWidgets.manage_drug_products(parent = self, ignore_OK_button = True)
     843
     844          #----------------------------------------------------------------
     845 -        def _on_manage_doses_button_pressed(self, event):
     846                  gmSubstanceMgmtWidgets.manage_substance_doses(parent = self)
     847
     848          #----------------------------------------------------------------
     849 -        def _on_heart_button_pressed(self, event):
     850                  gmNetworkTools.open_url_in_browser(url = gmMedication.URL_long_qt)
     851
     852          #----------------------------------------------------------------
     853 -        def _on_kidneys_button_pressed(self, event):
     854                  if self._PRW_drug.GetData() is None:
     855                          search_term = self._PRW_drug.GetValue().strip()
     856                  else:
     857                          search_term = self._PRW_drug.GetData(as_instance = True)
     858
     859                  gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
     860
     861          #----------------------------------------------------------------
     862 -        def _on_discontinued_as_planned_button_pressed(self, event):
     863
     864                  now = gmDateTime.pydt_now_here()
     865
     866                  self.__refresh_precautions()
     867
     868                  if self.data is None:
     869                          return
     870
     871                  # do we have a (full) plan ?
     872                  if None not in [self.data['started'], self.data['duration']]:
     873                          planned_end = self.data['started'] + self.data['duration']
     874                          # the plan hasn't ended so [Per plan] can't apply ;-)
     875                          if planned_end > now:
     876                                  return
     877                          self._DP_discontinued.SetData(planned_end)
     878                          self._PRW_discontinue_reason.Enable(True)
     879                          self._PRW_discontinue_reason.SetValue('')
     880                          return
     881
     882                  # we know started but not duration: apparently the plan is to stop today
     883                  if self.data['started'] is not None:
     884                          # but we haven't started yet so we can't stop
     885                          if self.data['started'] > now:
     886                                  return
     887
     888                  self._DP_discontinued.SetData(now)
     889                  self._PRW_discontinue_reason.Enable(True)
     890                  self._PRW_discontinue_reason.SetValue('')
     891
     892          #----------------------------------------------------------------
     893 -        def _on_chbox_long_term_checked(self, event):
     894                  if self._CHBOX_long_term.IsChecked() is True:
     895                          self._PRW_duration.Enable(False)
     896                          self._BTN_discontinued_as_planned.Enable(False)
     897                          self._PRW_discontinue_reason.Enable(False)
     898                  else:
     899                          self._PRW_duration.Enable(True)
     900                          self._BTN_discontinued_as_planned.Enable(True)
     901                          self._PRW_discontinue_reason.Enable(True)
     902
     903                  self.__refresh_precautions()
     904
     905          #----------------------------------------------------------------
     906 -        def _on_start_unknown_checked(self, event):
     907                  event.Skip()
     908                  if self._CHBOX_start_unknown.IsChecked() is True:
     909                          self._DP_started.Enable(False)
     910                          self._PRW_start_certainty.Enable(False)
     911                  else:
     912                          self._DP_started.Enable(True)
     913                          self._PRW_start_certainty.Enable(True)
     914
     915                  self.__refresh_precautions()
     916
     917          #----------------------------------------------------------------
     918 -        def turn_into_allergy(self, data=None):
     919                  if not self.save():
     920                          return False
     921
     922                  return turn_substance_intake_into_allergy (
     923                          parent = self,
     924                          intake = self.data,
     925                          emr = gmPerson.gmCurrentPatient().emr
     926                  )
     927
     928  #============================================================
     929 -def delete_substance_intake(parent=None, intake=None):
     930
     931          comps = intake.containing_drug.components
     932          if len(comps) > 1:
     933                  msg = _(
     934                          'This intake is part of a multi-component drug product:\n'
     935                          '\n'
     936                          ' %s\n'
     937                          '\n'
     938                          'Really delete all intakes related to this drug product ?'
     939                  ) % '\n '.join (
     940                          [ '%s %s%s' % (c['substance'], c['amount'], c.formatted_units) for c in comps ]
     941                  )
     942                  delete_all = gmGuiHelpers.gm_show_question (
     943                          title = _('Deleting medication / substance intake'),
     944                          question = msg
     945                  )
     946                  if not delete_all:
     947                          return
     948
     949          msg = _(
     950                  '\n'
     951                  '[%s]\n'
     952                  '\n'
     953                  'It may be prudent to edit (before deletion) the details\n'
     954                  'of this substance intake entry so as to leave behind\n'
     955                  'some indication of why it was deleted.\n'
     956          ) % intake.format()
     957
     958          dlg = gmGuiHelpers.c3ButtonQuestionDlg (
     959                  parent,
     960                  -1,
     961                  caption = _('Deleting medication / substance intake'),
     962                  question = msg,
     963                  button_defs = [
     964                          {'label': _('&Edit'), 'tooltip': _('Allow editing of substance intake entry before deletion.'), 'default': True},
     965                          {'label': _('&Delete'), 'tooltip': _('Delete immediately without editing first.')},
     966                          {'label': _('&Cancel'), 'tooltip': _('Abort. Do not delete or edit substance intake entry.')}
     967                  ]
     968          )
     969
     970          edit_first = dlg.ShowModal()
     971          dlg.DestroyLater()
     972
     973          if edit_first == wx.ID_CANCEL:
     974                  return
     975
     976          if edit_first == wx.ID_YES:
     977                  edit_intake_of_substance(parent = parent, substance = intake)
     978                  delete_it = gmGuiHelpers.gm_show_question (
     979                          aMessage = _('Now delete substance intake entry ?'),
     980                          aTitle = _('Deleting medication / substance intake')
     981                  )
     982          else:
     983                  delete_it = True
     984
     985          if not delete_it:
     986                  return
     987
     988          gmMedication.delete_substance_intake(pk_intake = intake['pk_substance_intake'], delete_siblings = True)
     989
     990  #------------------------------------------------------------
     991 -def edit_intake_of_substance(parent = None, substance=None, single_entry=False):
     992          ea = cSubstanceIntakeEAPnl(parent, -1, substance = substance)
     993          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     994          dlg.SetTitle(gmTools.coalesce(substance, _('Adding medication/non-medication substance intake'), _('Editing medication/non-medication substance intake')))
     995          dlg.left_extra_button = (
     996                  _('Allergy'),
     997                  _('Document an allergy against this substance.'),
     998                  ea.turn_into_allergy
     999          )
    1000          dlg.SetSize((650,500))
    1001          if dlg.ShowModal() == wx.ID_OK:
    1002                  dlg.DestroyLater()
    1003                  return True
    1004          dlg.DestroyLater()
    1005          return False
    1006
    1007  #============================================================
    1008  # current substances grid
    1009  #------------------------------------------------------------
    1010 -def configure_medication_list_template(parent=None):
    1011
    1012          if parent is None:
    1013                  parent = wx.GetApp().GetTopWindow()
    1014
    1015          template = gmFormWidgets.manage_form_templates (
    1016                  parent = parent,
    1017                  template_types = ['current medication list']
    1018          )
    1019          option = 'form_templates.medication_list'
    1020
    1021          if template is None:
    1022                  gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
    1023                  return None
    1024
    1025          if template['engine'] not in ['L', 'X', 'T']:
    1026                  gmDispatcher.send(signal = 'statustext', msg = _('No medication list template configured.'), beep = True)
    1027                  return None
    1028
    1029          dbcfg = gmCfg.cCfgSQL()
    1030          dbcfg.set (
    1031                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1032                  option = option,
    1033                  value = '%s - %s' % (template['name_long'], template['external_version'])
    1034          )
    1035
    1036          return template
    1037
    1038  #------------------------------------------------------------
    1039 -def print_medication_list(parent=None):
    1040
    1041          if parent is None:
    1042                  parent = wx.GetApp().GetTopWindow()
    1043
    1044          # 1) get template
    1045          dbcfg = gmCfg.cCfgSQL()
    1046          option = 'form_templates.medication_list'
    1047
    1048          template = dbcfg.get2 (
    1049                  option = option,
    1050                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1051                  bias = 'user'
    1052          )
    1053
    1054          if template is None:
    1055                  template = configure_medication_list_template(parent = parent)
    1056                  if template is None:
    1057                          gmGuiHelpers.gm_show_error (
    1058                                  aMessage = _('There is no medication list template configured.'),
    1059                                  aTitle = _('Printing medication list')
    1060                          )
    1061                          return False
    1062          else:
    1063                  try:
    1064                          name, ver = template.split(' - ')
    1065                  except Exception:
    1066                          _log.exception('problem splitting medication list template name [%s]', template)
    1067                          gmDispatcher.send(signal = 'statustext', msg = _('Problem loading medication list template.'), beep = True)
    1068                          return False
    1069                  template = gmForms.get_form_template(name_long = name, external_version = ver)
    1070                  if template is None:
    1071                          gmGuiHelpers.gm_show_error (
    1072                                  aMessage = _('Cannot load medication list template [%s - %s]') % (name, ver),
    1073                                  aTitle = _('Printing medication list')
    1074                          )
    1075                          return False
    1076
    1077          # 2) process template
    1078          meds_list = gmFormWidgets.generate_form_from_template (
    1079                  parent = parent,
    1080                  template = template,
    1081                  edit = False
    1082          )
    1083          if meds_list is None:
    1084                  return False
    1085
    1086          # 3) print template
    1087          return gmFormWidgets.act_on_generated_forms (
    1088                  parent = parent,
    1089                  forms = [meds_list],
    1090                  jobtype = 'medication_list',
    1091                  #episode_name = u'administrative',
    1092                  episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
    1093                  progress_note = _('generated medication list document'),
    1094                  review_copy_as_normal = True
    1095          )
    1096
    1097  #------------------------------------------------------------
    1098 -def configure_prescription_template(parent=None):
    1099
    1100          if parent is None:
    1101                  parent = wx.GetApp().GetTopWindow()
    1102
    1103          template = gmFormWidgets.manage_form_templates (
    1104                  parent = parent,
    1105                  msg = _('Select the default prescription template:'),
    1106                  template_types = ['prescription', 'current medication list']
    1107          )
    1108
    1109          if template is None:
    1110                  gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
    1111                  return None
    1112
    1113          if template['engine'] not in ['L', 'X', 'T']:
    1114                  gmDispatcher.send(signal = 'statustext', msg = _('No prescription template configured.'), beep = True)
    1115                  return None
    1116
    1117          option = 'form_templates.prescription'
    1118          dbcfg = gmCfg.cCfgSQL()
    1119          dbcfg.set (
    1120                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1121                  option = option,
    1122                  value = '%s - %s' % (template['name_long'], template['external_version'])
    1123          )
    1124
    1125          return template
    1126
    1127  #------------------------------------------------------------
    1128 -def get_prescription_template(parent=None):
    1129
    1130          if parent is None:
    1131                  parent = wx.GetApp().GetTopWindow()
    1132
    1133          dbcfg = gmCfg.cCfgSQL()
    1134          option = 'form_templates.prescription'
    1135          template_name = dbcfg.get2 (
    1136                  option = option,
    1137                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1138                  bias = 'user'
    1139          )
    1140
    1141          if template_name is None:
    1142                  template = configure_prescription_template(parent = parent)
    1143                  if template is None:
    1144                          gmGuiHelpers.gm_show_error (
    1145                                  aMessage = _('There is no prescription template configured.'),
    1146                                  aTitle = _('Printing prescription')
    1147                          )
    1148                          return None
    1149                  return template
    1150
    1151          try:
    1152                  name, ver = template_name.split(' - ')
    1153          except Exception:
    1154                  _log.exception('problem splitting prescription template name [%s]', template_name)
    1155                  gmDispatcher.send(signal = 'statustext', msg = _('Problem loading prescription template.'), beep = True)
    1156                  return False
    1157          template = gmForms.get_form_template(name_long = name, external_version = ver)
    1158          if template is None:
    1159                  gmGuiHelpers.gm_show_error (
    1160                          aMessage = _('Cannot load prescription template [%s - %s]') % (name, ver),
    1161                          aTitle = _('Printing prescription')
    1162                  )
    1163                  return None
    1164          return template
    1165
    1166  #------------------------------------------------------------
    1167 -def print_prescription(parent=None, emr=None):
    1168
    1169          # 1) get template
    1170          rx_template = get_prescription_template(parent = parent)
    1171          if rx_template is None:
    1172                  return False
    1173
    1174          # 2) process template
    1175          rx = gmFormWidgets.generate_form_from_template (
    1176                  parent = parent,
    1177                  template = rx_template,
    1178                  edit = False
    1179          )
    1180          if rx is None:
    1181                  return False
    1182
    1183          # 3) print template
    1184          return gmFormWidgets.act_on_generated_forms (
    1185                  parent = parent,
    1186                  forms = [rx],
    1187                  jobtype = 'prescription',
    1188                  #episode_name = u'administrative',
    1189                  episode_name = gmMedication.DEFAULT_MEDICATION_HISTORY_EPISODE,
    1190                  progress_note = _('generated prescription'),
    1191                  review_copy_as_normal = True
    1192          )
    1193
    1194  #------------------------------------------------------------
    1195 -def prescribe_drugs(parent=None, emr=None):
    1196
    1197          dbcfg = gmCfg.cCfgSQL()
    1198          rx_mode = dbcfg.get2 (
    1199                  option = 'horst_space.default_prescription_mode',
    1200                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1201                  bias = 'user',
    1202                  default = 'form'                        # set to 'database' to access database
    1203          )
    1204
    1205          if parent is None:
    1206                  parent = wx.GetApp().GetTopWindow()
    1207
    1208          if rx_mode == 'form':
    1209                  return print_prescription(parent = parent, emr = emr)
    1210
    1211          if rx_mode == 'database':
    1212                  drug_db = gmSubstanceMgmtWidgets.get_drug_database()            #gmPerson.gmCurrentPatient() xxxxxxx ?
    1213                  if drug_db is None:
    1214                          return
    1215                  drug_db.reviewer = gmStaff.gmCurrentProvider()
    1216                  prescribed_drugs = drug_db.prescribe()
    1217                  update_substance_intake_list_from_prescription (
    1218                          parent = parent,
    1219                          prescribed_drugs = prescribed_drugs,
    1220                          emr = emr
    1221                  )
    1222
    1223  #------------------------------------------------------------
    1224 -def update_substance_intake_list_from_prescription(parent=None, prescribed_drugs=None, emr=None):
    1225
    1226          if len(prescribed_drugs) == 0:
    1227                  return
    1228
    1229          curr_meds =  [ i['pk_drug_product'] for i in emr.get_current_medications() if i['pk_drug_product'] is not None ]
    1230          new_drugs = []
    1231          for drug in prescribed_drugs:
    1232                  if drug['pk_drug_product'] not in curr_meds:
    1233                          new_drugs.append(drug)
    1234
    1235          if len(new_drugs) == 0:
    1236                  return
    1237
    1238          if parent is None:
    1239                  parent = wx.GetApp().GetTopWindow()
    1240
    1241          picker = gmListWidgets.cItemPickerDlg (
    1242                  parent,
    1243                  -1,
    1244                  msg = _(
    1245                          'These products have been prescribed but are not listed\n'
    1246                          'in the current medication list of this patient.\n'
    1247                          '\n'
    1248                          'Please select those you want added to the medication list.'
    1249                  )
    1250          )
    1251          picker.set_columns (
    1252                  columns = [_('Newly prescribed drugs')],
    1253                  columns_right = [_('Add to medication list')]
    1254          )
    1255          choices = [ ('%s %s (%s)' % (d['product'], d['l10n_preparation'], '; '.join(d['components']))) for d in new_drugs ]
    1256          picker.set_choices (
    1257                  choices = choices,
    1258                  data = new_drugs
    1259          )
    1260          picker.ShowModal()
    1261          drugs2add = picker.get_picks()
    1262          picker.DestroyLater()
    1263
    1264          if drugs2add is None:
    1265                  return
    1266
    1267          if len(drugs2add) == 0:
    1268                  return
    1269
    1270          for drug in drugs2add:
    1271                  # only add first component since all other components get added by a trigger ...
    1272                  intake = emr.add_substance_intake(pk_component = drug['components'][0]['pk_component'])
    1273                  if intake is None:
    1274                          continue
    1275                  intake['intake_is_approved_of'] = True
    1276                  intake.save()
    1277
    1278          return
    1279
    1280  #------------------------------------------------------------
    1281 -class cCurrentSubstancesGrid(wx.grid.Grid):
    1282          """A grid class for displaying current substance intake.
    1283
    1284          - does NOT listen to the currently active patient
    1285          - thereby it can display any patient at any time
    1286          """
    1287 -        def __init__(self, *args, **kwargs):
    1288
    1289                  wx.grid.Grid.__init__(self, *args, **kwargs)
    1290
    1291                  self.__patient = None
    1292                  self.__row_data = {}
    1293                  self.__prev_row = None
    1294                  self.__prev_tooltip_row = None
    1295                  self.__prev_cell_0 = None
    1296                  self.__grouping_mode = 'issue'
    1297                  self.__filter_show_unapproved = True
    1298                  self.__filter_show_inactive = True
    1299
    1300                  self.__grouping2col_labels = {
    1301                          'issue': [
    1302                                  _('Health issue'),
    1303                                  _('Substance'),
    1304                                  _('Strength'),
    1305                                  _('Schedule'),
    1306                                  _('Timeframe'),
    1307                                  _('Product'),
    1308                                  _('Advice')
    1309                          ],
    1310                          'product': [
    1311                                  _('Product'),
    1312                                  _('Schedule'),
    1313                                  _('Substance'),
    1314                                  _('Strength'),
    1315                                  _('Timeframe'),
    1316                                  _('Health issue'),
    1317                                  _('Advice')
    1318                          ],
    1319                          'episode': [
    1320                                  _('Episode'),
    1321                                  _('Substance'),
    1322                                  _('Strength'),
    1323                                  _('Schedule'),
    1324                                  _('Timeframe'),
    1325                                  _('Product'),
    1326                                  _('Advice')
    1327                          ],
    1328                          'start': [
    1329                                  _('Episode'),
    1330                                  _('Substance'),
    1331                                  _('Strength'),
    1332                                  _('Schedule'),
    1333                                  _('Timeframe'),
    1334                                  _('Product'),
    1335                                  _('Advice')
    1336                          ],
    1337                  }
    1338
    1339                  self.__grouping2order_by_clauses = {
    1340                          'issue': 'pk_health_issue NULLS FIRST, substance, started',
    1341                          'episode': 'pk_health_issue NULLS FIRST, episode, substance, started',
    1342                          'product': 'product NULLS LAST, substance, started',
    1343                          'start': 'started DESC, substance, episode'
    1344                  }
    1345
    1346                  self.__init_ui()
    1347                  self.__register_events()
    1348
    1349          #------------------------------------------------------------
    1350          # external API
    1351          #------------------------------------------------------------
    1352 -        def get_selected_cells(self):
    1353
    1354                  sel_block_top_left = self.GetSelectionBlockTopLeft()
    1355                  sel_block_bottom_right = self.GetSelectionBlockBottomRight()
    1356                  sel_cols = self.GetSelectedCols()
    1357                  sel_rows = self.GetSelectedRows()
    1358
    1359                  selected_cells = []
    1360
    1361                  # individually selected cells (ctrl-click)
    1362                  selected_cells += self.GetSelectedCells()
    1363
    1364                  # selected rows
    1365                  selected_cells += list (
    1366                          (row, col)
    1367                                  for row in sel_rows
    1368                                  for col in range(self.GetNumberCols())
    1369                  )
    1370
    1371                  # selected columns
    1372                  selected_cells += list (
    1373                          (row, col)
    1374                                  for row in range(self.GetNumberRows())
    1375                                  for col in sel_cols
    1376                  )
    1377
    1378                  # selection blocks
    1379                  for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
    1380                          selected_cells += [
    1381                                  (row, col)
    1382                                          for row in range(top_left[0], bottom_right[0] + 1)
    1383                                          for col in range(top_left[1], bottom_right[1] + 1)
    1384                          ]
    1385
    1386                  return set(selected_cells)
    1387
    1388          #------------------------------------------------------------
    1389 -        def get_selected_rows(self):
    1390                  rows = {}
    1391
    1392                  for row, col in self.get_selected_cells():
    1393                          rows[row] = True
    1394
    1395                  return rows.keys()
    1396
    1397          #------------------------------------------------------------
    1398 -        def get_selected_data(self):
    1399                  return [ self.__row_data[row] for row in self.get_selected_rows() ]
    1400
    1401          #------------------------------------------------------------
    1402 -        def get_row_data(self):
    1403                  return self.__row_data.values()
    1404
    1405          #------------------------------------------------------------
    1406 -        def repopulate_grid(self):
    1407
    1408                  self.empty_grid()
    1409
    1410                  if self.__patient is None:
    1411                          return
    1412
    1413                  emr = self.__patient.emr
    1414                  meds = emr.get_current_medications (
    1415                          order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
    1416                          include_unapproved = self.__filter_show_unapproved,
    1417                          include_inactive = self.__filter_show_inactive
    1418                  )
    1419                  if not meds:
    1420                          return
    1421
    1422                  self.BeginBatch()
    1423
    1424                  # columns
    1425                  labels = self.__grouping2col_labels[self.__grouping_mode]
    1426                  if self.__filter_show_unapproved:
    1427                          self.AppendCols(numCols = len(labels) + 1)
    1428                  else:
    1429                          self.AppendCols(numCols = len(labels))
    1430                  for col_idx in range(len(labels)):
    1431                          self.SetColLabelValue(col_idx, labels[col_idx])
    1432                  if self.__filter_show_unapproved:
    1433                          #self.SetColLabelValue(len(labels), u'OK?')
    1434                          self.SetColLabelValue(len(labels), '')
    1435                          self.SetColSize(len(labels), 40)
    1436
    1437                  self.AppendRows(numRows = len(meds))
    1438
    1439                  # loop over data
    1440                  for row_idx in range(len(meds)):
    1441                          med = meds[row_idx]
    1442                          self.__row_data[row_idx] = med
    1443
    1444                          if med['is_currently_active'] is True:
    1445                                  atcs = []
    1446                                  if med['atc_substance'] is not None:
    1447                                          atcs.append(med['atc_substance'])
    1448                                  allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
    1449                                  if allg not in [None, False]:
    1450                                          attr = self.GetOrCreateCellAttr(row_idx, 0)
    1451                                          if allg['type'] == 'allergy':
    1452                                                  attr.SetTextColour('red')
    1453                                          else:
    1454                                                  #attr.SetTextColour('yellow')           # too light
    1455                                                  #attr.SetTextColour('pink')                     # too light
    1456                                                  #attr.SetTextColour('dark orange')      # slightly better
    1457                                                  attr.SetTextColour('magenta')
    1458                                          self.SetRowAttr(row_idx, attr)
    1459                          else:
    1460                                  attr = self.GetOrCreateCellAttr(row_idx, 0)
    1461                                  attr.SetTextColour('grey')
    1462                                  self.SetRowAttr(row_idx, attr)
    1463
    1464                          if self.__grouping_mode in ['episode', 'start']:
    1465                                  if med['pk_episode'] is None:
    1466                                          self.__prev_cell_0 = None
    1467                                          epi = gmTools.u_diameter
    1468                                  else:
    1469                                          if self.__prev_cell_0 == med['episode']:
    1470                                                  epi = ''
    1471                                          else:
    1472                                                  self.__prev_cell_0 = med['episode']
    1473                                                  epi = gmTools.coalesce(med['episode'], '')
    1474                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
    1475
    1476                                  self.SetCellValue(row_idx, 1, med['substance'])
    1477                                  self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
    1478                                  self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
    1479                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1480
    1481                                  if med['pk_drug_product'] is None:
    1482                                          product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
    1483                                  else:
    1484                                          if med['is_fake_product']:
    1485                                                  product = '%s (%s)' % (
    1486                                                          gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1487                                                          med['l10n_preparation']
    1488                                                  )
    1489                                          else:
    1490                                                  product = '%s (%s)' % (
    1491                                                          gmTools.coalesce(med['product'], ''),
    1492                                                          med['l10n_preparation']
    1493                                                  )
    1494                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
    1495
    1496                          elif self.__grouping_mode == 'issue':
    1497                                  if med['pk_health_issue'] is None:
    1498                                          self.__prev_cell_0 = None
    1499                                          issue = '%s%s' % (
    1500                                                  gmTools.u_diameter,
    1501                                                  gmTools.coalesce(med['episode'], '', ' (%s)')
    1502                                          )
    1503                                  else:
    1504                                          if self.__prev_cell_0 == med['health_issue']:
    1505                                                  issue = ''
    1506                                          else:
    1507                                                  self.__prev_cell_0 = med['health_issue']
    1508                                                  issue = med['health_issue']
    1509                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
    1510
    1511                                  self.SetCellValue(row_idx, 1, med['substance'])
    1512                                  self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
    1513                                  self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
    1514                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1515
    1516                                  if med['pk_drug_product'] is None:
    1517                                          product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
    1518                                  else:
    1519                                          if med['is_fake_product']:
    1520                                                  product = '%s (%s)' % (
    1521                                                          gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1522                                                          med['l10n_preparation']
    1523                                                  )
    1524                                          else:
    1525                                                  product = '%s (%s)' % (
    1526                                                          gmTools.coalesce(med['product'], ''),
    1527                                                          med['l10n_preparation']
    1528                                                  )
    1529                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
    1530
    1531                          elif self.__grouping_mode == 'product':
    1532
    1533                                  if med['pk_drug_product'] is None:
    1534                                          self.__prev_cell_0 = None
    1535                                          product =  '%s (%s)' % (
    1536                                                  gmTools.u_diameter,
    1537                                                  med['l10n_preparation']
    1538                                          )
    1539                                  else:
    1540                                          if self.__prev_cell_0 == med['product']:
    1541                                                  product = ''
    1542                                          else:
    1543                                                  self.__prev_cell_0 = med['product']
    1544                                                  if med['is_fake_product']:
    1545                                                          product = '%s (%s)' % (
    1546                                                                  gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1547                                                                  med['l10n_preparation']
    1548                                                          )
    1549                                                  else:
    1550                                                          product = '%s (%s)' % (
    1551                                                                  gmTools.coalesce(med['product'], ''),
    1552                                                                  med['l10n_preparation']
    1553                                                          )
    1554                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = product, width = 35))
    1555
    1556                                  self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], ''))
    1557                                  self.SetCellValue(row_idx, 2, med['substance'])
    1558                                  self.SetCellValue(row_idx, 3, '%s %s' % (med['amount'], med['unit']))
    1559                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1560
    1561                                  if med['pk_health_issue'] is None:
    1562                                          issue = '%s%s' % (
    1563                                                  gmTools.u_diameter,
    1564                                                  gmTools.coalesce(med['episode'], '', ' (%s)')
    1565                                          )
    1566                                  else:
    1567                                          issue = gmTools.coalesce(med['health_issue'], '')
    1568                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = issue, width = 40))
    1569
    1570                          else:
    1571                                  raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
    1572
    1573                          if med['notes'] is not None:
    1574                                  self.SetCellValue(row_idx, 6, gmTools.wrap(text = med['notes'], width = 50))
    1575
    1576                          if self.__filter_show_unapproved:
    1577                                  self.SetCellValue (
    1578                                          row_idx,
    1579                                          len(labels),
    1580                                          gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, gmTools.u_frowning_face, '?')
    1581                                  )
    1582                                  font = self.GetCellFont(row_idx, len(labels))
    1583                                  font.SetPointSize(font.GetPointSize() + 2)
    1584                                  self.SetCellFont(row_idx, len(labels), font)
    1585
    1586                          #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
    1587
    1588                  self.AutoSize()
    1589                  self.EndBatch()
    1590          #------------------------------------------------------------
    1591 -        def empty_grid(self):
    1592                  self.BeginBatch()
    1593                  self.ClearGrid()
    1594                  # Windows cannot do "nothing", it rather decides to assert()
    1595                  # on thinking it is supposed to do nothing
    1596                  if self.GetNumberRows() > 0:
    1597                          self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
    1598                  if self.GetNumberCols() > 0:
    1599                          self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
    1600                  self.EndBatch()
    1601                  self.__row_data = {}
    1602                  self.__prev_cell_0 = None
    1603
    1604          #------------------------------------------------------------
    1605 -        def show_info_on_entry(self):
    1606
    1607                  if len(self.__row_data) == 0:
    1608                          return
    1609
    1610                  sel_rows = self.get_selected_rows()
    1611                  if len(sel_rows) != 1:
    1612                          return
    1613
    1614                  drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
    1615                  if drug_db is None:
    1616                          return
    1617
    1618                  intake = self.get_selected_data()[0]            # just in case
    1619                  if intake['product'] is None:
    1620                          drug_db.show_info_on_substance(substance_intake = intake)
    1621                  else:
    1622                          drug_db.show_info_on_drug(substance_intake = intake)
    1623
    1624          #------------------------------------------------------------
    1625 -        def show_renal_insufficiency_info(self):
    1626                  search_term = None
    1627                  if len(self.__row_data) > 0:
    1628                          sel_rows = self.get_selected_rows()
    1629                          if len(sel_rows) == 1:
    1630                                  search_term = self.get_selected_data()[0]
    1631                  gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
    1632
    1633          #------------------------------------------------------------
    1634 -        def show_cardiac_info(self):
    1635                  gmNetworkTools.open_url_in_browser(url = gmMedication.URL_long_qt)
    1636
    1637          #------------------------------------------------------------
    1638 -        def report_ADR(self):
    1639                  dbcfg = gmCfg.cCfgSQL()
    1640                  url = dbcfg.get2 (
    1641                          option = 'external.urls.report_ADR',
    1642                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1643                          bias = 'user',
    1644                          default = gmMedication.URL_drug_adr_german_default
    1645                  )
    1646                  gmNetworkTools.open_url_in_browser(url = url)
    1647
    1648          #------------------------------------------------------------
    1649 -        def prescribe(self):
    1650                  prescribe_drugs (
    1651                          parent = self,
    1652                          emr = self.__patient.emr
    1653                  )
    1654          #------------------------------------------------------------
    1655 -        def check_interactions(self):
    1656
    1657                  if len(self.__row_data) == 0:
    1658                          return
    1659
    1660                  drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
    1661                  if drug_db is None:
    1662                          return
    1663
    1664                  if len(self.get_selected_rows()) > 1:
    1665                          drug_db.check_interactions(substance_intakes = self.get_selected_data())
    1666                  else:
    1667                          drug_db.check_interactions(substance_intakes = self.__row_data.values())
    1668          #------------------------------------------------------------
    1669 -        def add_substance(self):
    1670                  edit_intake_of_substance(parent = self, substance = None)
    1671          #------------------------------------------------------------
    1672 -        def edit_substance(self):
    1673
    1674                  rows = self.get_selected_rows()
    1675
    1676                  if len(rows) == 0:
    1677                          return
    1678
    1679                  if len(rows) > 1:
    1680                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
    1681                          return
    1682
    1683                  subst = self.get_selected_data()[0]
    1684                  edit_intake_of_substance(parent = self, substance = subst)
    1685
    1686          #------------------------------------------------------------
    1687 -        def delete_intake(self):
    1688
    1689                  rows = self.get_selected_rows()
    1690
    1691                  if len(rows) == 0:
    1692                          return
    1693
    1694                  if len(rows) > 1:
    1695                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
    1696                          return
    1697
    1698                  intake = self.get_selected_data()[0]
    1699                  delete_substance_intake(parent = self, intake = intake)
    1700
    1701          #------------------------------------------------------------
    1702 -        def create_allergy_from_substance(self):
    1703                  rows = self.get_selected_rows()
    1704
    1705                  if len(rows) == 0:
    1706                          return
    1707
    1708                  if len(rows) > 1:
    1709                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
    1710                          return
    1711
    1712                  return turn_substance_intake_into_allergy (
    1713                          parent = self,
    1714                          intake = self.get_selected_data()[0],
    1715                          emr = self.__patient.emr
    1716                  )
    1717          #------------------------------------------------------------
    1718 -        def print_medication_list(self):
    1719                  # there could be some filtering/user interaction going on here
    1720                  print_medication_list(parent = self)
    1721          #------------------------------------------------------------
    1722 -        def get_row_tooltip(self, row=None):
    1723
    1724                  try:
    1725                          entry = self.__row_data[row]
    1726                  except KeyError:
    1727                          return ' '
    1728
    1729                  emr = self.__patient.emr
    1730                  atcs = []
    1731                  if entry['atc_substance'] is not None:
    1732                          atcs.append(entry['atc_substance'])
    1733  #               if entry['atc_drug'] is not None:
    1734  #                       atcs.append(entry['atc_drug'])
    1735  #               allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), product_name = entry['product'])
    1736                  allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
    1737
    1738                  tt = _('Substance intake entry (%s, %s)   [#%s]                     \n') % (
    1739                          gmTools.bool2subst (
    1740                                  boolean = entry['is_currently_active'],
    1741                                  true_return = gmTools.bool2subst (
    1742                                          boolean = entry['seems_inactive'],
    1743                                          true_return = _('active, needs check'),
    1744                                          false_return = _('active'),
    1745                                          none_return = _('assumed active')
    1746                                  ),
    1747                                  false_return = _('inactive')
    1748                          ),
    1749                          gmTools.bool2subst (
    1750                                  boolean = entry['intake_is_approved_of'],
    1751                                  true_return = _('approved'),
    1752                                  false_return = _('unapproved')
    1753                          ),
    1754                          entry['pk_substance_intake']
    1755                  )
    1756
    1757                  if allg not in [None, False]:
    1758                          certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
    1759                          tt += '\n'
    1760                          tt += ' !! ---- Cave ---- !!\n'
    1761                          tt += ' %s (%s): %s (%s)\n' % (
    1762                                  allg['l10n_type'],
    1763                                  certainty,
    1764                                  allg['descriptor'],
    1765                                  gmTools.coalesce(allg['reaction'], '')[:40]
    1766                          )
    1767                          tt += '\n'
    1768
    1769                  tt += ' ' + _('Substance: %s   [#%s]\n') % (entry['substance'], entry['pk_substance'])
    1770                  tt += ' ' + _('Preparation: %s\n') % entry['l10n_preparation']
    1771                  tt += ' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit'])
    1772                  tt += '\n'
    1773                  tt += gmTools.coalesce(entry['atc_substance'], '', _(' ATC (substance): %s\n'))
    1774
    1775                  tt += '\n'
    1776
    1777                  tt += gmTools.coalesce (
    1778                          entry['product'],
    1779                          '',
    1780                          _(' Product name: %%s   [#%s]\n') % entry['pk_drug_product']
    1781                  )
    1782                  tt += gmTools.coalesce(entry['atc_drug'], '', _(' ATC (drug): %s\n'))
    1783
    1784                  tt += '\n'
    1785
    1786                  tt += gmTools.coalesce(entry['schedule'], '', _(' Regimen: %s\n'))
    1787
    1788                  if entry['is_long_term']:
    1789                          duration = ' %s %s' % (gmTools.u_arrow2right, gmTools.u_infinity)
    1790                  else:
    1791                          if entry['duration'] is None:
    1792                                  duration = ''
    1793                          else:
    1794                                  duration = ' %s %s' % (gmTools.u_arrow2right, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
    1795
    1796                  tt += _(' Started %s%s%s\n') % (
    1797                          entry.medically_formatted_start,
    1798                          duration,
    1799                          gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), '')
    1800                  )
    1801
    1802                  if entry['discontinued'] is not None:
    1803                          tt += _(' Discontinued %s\n') % gmDateTime.pydt_strftime(entry['discontinued'], '%Y %b %d')
    1804                          tt += gmTools.coalesce(entry['discontinue_reason'], '', _(' Reason: %s\n'))
    1805
    1806                  tt += '\n'
    1807
    1808                  tt += gmTools.coalesce(entry['aim'], '', _(' Aim: %s\n'))
    1809                  tt += gmTools.coalesce(entry['episode'], '', _(' Episode: %s\n'))
    1810                  tt += gmTools.coalesce(entry['health_issue'], '', _(' Health issue: %s\n'))
    1811                  tt += gmTools.coalesce(entry['notes'], '', _(' Advice: %s\n'))
    1812
    1813                  tt += '\n'
    1814
    1815                  tt += _('Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
    1816                          'row_ver': entry['row_version'],
    1817                          'mod_when': gmDateTime.pydt_strftime(entry['modified_when'], '%Y %b %d  %H:%M:%S'),
    1818                          'mod_by': entry['modified_by']
    1819                  })
    1820
    1821                  return tt
    1822
    1823          #------------------------------------------------------------
    1824          # internal helpers
    1825          #------------------------------------------------------------
    1826 -        def __init_ui(self):
    1827                  self.CreateGrid(0, 1)
    1828                  self.EnableEditing(0)
    1829                  self.EnableDragGridSize(1)
    1830                  self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
    1831
    1832                  self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
    1833
    1834                  self.SetRowLabelSize(0)
    1835                  self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
    1836
    1837          #------------------------------------------------------------
    1838          # properties
    1839          #------------------------------------------------------------
    1840 -        def _get_patient(self):
    1841                  return self.__patient
    1842
    1843 -        def _set_patient(self, patient):
    1844                  self.__patient = patient
    1845                  self.repopulate_grid()
    1846
    1847          patient = property(_get_patient, _set_patient)
    1848          #------------------------------------------------------------
    1849 -        def _get_grouping_mode(self):
    1850                  return self.__grouping_mode
    1851
    1852 -        def _set_grouping_mode(self, mode):
    1853                  self.__grouping_mode = mode
    1854                  self.repopulate_grid()
    1855
    1856          grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
    1857          #------------------------------------------------------------
    1858 -        def _get_filter_show_unapproved(self):
    1859                  return self.__filter_show_unapproved
    1860
    1861 -        def _set_filter_show_unapproved(self, val):
    1862                  self.__filter_show_unapproved = val
    1863                  self.repopulate_grid()
    1864
    1865          filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
    1866          #------------------------------------------------------------
    1867 -        def _get_filter_show_inactive(self):
    1868                  return self.__filter_show_inactive
    1869
    1870 -        def _set_filter_show_inactive(self, val):
    1871                  self.__filter_show_inactive = val
    1872                  self.repopulate_grid()
    1873
    1874          filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
    1875          #------------------------------------------------------------
    1876          # event handling
    1877          #------------------------------------------------------------
    1878 -        def __register_events(self):
    1879                  # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
    1880                  self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
    1881                  #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
    1882                  #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
    1883
    1884                  # editing cells
    1885                  self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
    1886          #------------------------------------------------------------
    1887 -        def __on_mouse_over_cells(self, evt):
    1888                  """Calculate where the mouse is and set the tooltip dynamically."""
    1889
    1890                  # Use CalcUnscrolledPosition() to get the mouse position within the
    1891                  # entire grid including what's offscreen
    1892                  x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
    1893
    1894                  # use this logic to prevent tooltips outside the actual cells
    1895                  # apply to GetRowSize, too
    1896  #        tot = 0
    1897  #        for col in range(self.NumberCols):
    1898  #            tot += self.GetColSize(col)
    1899  #            if xpos <= tot:
    1900  #                self.tool_tip.Tip = 'Tool tip for Column %s' % (
    1901  #                    self.GetColLabelValue(col))
    1902  #                break
    1903  #            else:  # mouse is in label area beyond the right-most column
    1904  #            self.tool_tip.Tip = ''
    1905
    1906                  row, col = self.XYToCell(x, y)
    1907
    1908                  if row == self.__prev_tooltip_row:
    1909                          return
    1910
    1911                  self.__prev_tooltip_row = row
    1912
    1913                  try:
    1914                          evt.GetEventObject().SetToolTip(self.get_row_tooltip(row = row))
    1915                  except KeyError:
    1916                          pass
    1917          #------------------------------------------------------------
    1918 -        def __on_cell_left_dclicked(self, evt):
    1919                  row = evt.GetRow()
    1920                  data = self.__row_data[row]
    1921                  edit_intake_of_substance(parent = self, substance = data)
    1922
    1923  #============================================================
    1924 -def configure_default_medications_lab_panel(parent=None):
    1925
    1926          panels = gmPathLab.get_test_panels(order_by = 'description')
    1927          gmCfgWidgets.configure_string_from_list_option (
    1928                  parent = parent,
    1929                  message = _(
    1930                          '\n'
    1931                          'Select the measurements panel to show in the medications plugin.'
    1932                          '\n'
    1933                  ),
    1934                  option = 'horstspace.medications_plugin.lab_panel',
    1935                  bias = 'user',
    1936                  default_value = None,
    1937                  choices = [ '%s%s' % (p['description'], gmTools.coalesce(p['comment'], '', ' (%s)')) for p in panels ],
    1938                  columns = [_('Measurements panel')],
    1939                  data = [ p['pk_test_panel'] for p in panels ],
    1940                  caption = _('Configuring medications plugin measurements panel')
    1941          )
    1942
    1943  #============================================================
    1944 -def configure_adr_url(self):
    1945
    1946          def is_valid(value):
    1947                  value = value.strip()
    1948                  if value == '':
    1949                          return True, gmMedication.URL_drug_adr_german_default
    1950                  try:
    1951                          urllib.request.urlopen(value)
    1952                          return True, value
    1953                  except Exception:
    1954                          return True, value
    1955
    1956          gmCfgWidgets.configure_string_option (
    1957                  message = _(
    1958                          'GNUmed will use this URL to access a website which lets\n'
    1959                          'you report an adverse drug reaction (ADR).\n'
    1960                          '\n'
    1961                          'If you leave this empty it will fall back\n'
    1962                          'to an URL for reporting ADRs in Germany.'
    1963                  ),
    1964                  option = 'external.urls.report_ADR',
    1965                  bias = 'user',
    1966                  default_value = gmMedication.URL_drug_adr_german_default,
    1967                  validator = is_valid
    1968          )
    1969
    1970  #============================================================
    1971  from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
    1972
    1973 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
    1974
    1975          """Panel holding a grid with current substances. Used as notebook page."""
    1976
    1977 -        def __init__(self, *args, **kwargs):
    1978
    1979                  wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
    1980                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1981
    1982                  self.__grouping_choice_labels = [
    1983                          {'label': _('Health issue'), 'data': 'issue'} ,
    1984                          {'label': _('Drug product'), 'data': 'product'},
    1985                          {'label': _('Episode'), 'data': 'episode'},
    1986                          {'label': _('Started'), 'data': 'start'}
    1987                  ]
    1988                  self.__lab_panel = None
    1989
    1990                  self.__init_ui()
    1991                  self.__register_interests()
    1992
    1993          #-----------------------------------------------------
    1994 -        def __init_ui(self):
    1995                  self._CHCE_grouping.Clear()
    1996                  for option in self.__grouping_choice_labels:
    1997                          self._CHCE_grouping.Append(option['label'], option['data'])
    1998                  self._CHCE_grouping.SetSelection(0)
    1999
    2000                  tt = self._BTN_heart.GetToolTipText()
    2001                  try:
    2002                          self._BTN_heart.SetToolTip(tt % gmMedication.URL_long_qt)
    2003                  except TypeError:
    2004                          _log.exception('translation error: %s', tt)
    2005
    2006                  tt = self._BTN_kidneys.GetToolTipText()
    2007                  try:
    2008                          self._BTN_kidneys.SetToolTip(tt % gmMedication.URL_renal_insufficiency)
    2009                  except TypeError:
    2010                          _log.exception('translation error: %s', tt)
    2011
    2012          #-----------------------------------------------------
    2013          # reget-on-paint mixin API
    2014          #-----------------------------------------------------
    2015 -        def _populate_with_data(self):
    2016                  """Populate cells with data from model."""
    2017                  pat = gmPerson.gmCurrentPatient()
    2018                  if pat.connected:
    2019                          self._grid_substances.patient = pat
    2020                          self.__refresh_gfr(pat)
    2021                          self.__refresh_lab(patient = pat)
    2022                  else:
    2023                          self._grid_substances.patient = None
    2024                          self.__clear_gfr()
    2025                          self.__refresh_lab(patient = None)
    2026                  return True
    2027
    2028          #--------------------------------------------------------
    2029 -        def __refresh_lab(self, patient):
    2030
    2031                  self._GSZR_lab.Clear(True)              # also delete child windows
    2032                  self._HLINE_lab.Hide()
    2033
    2034                  if patient is None:
    2035                          self.Layout()
    2036                          return
    2037
    2038                  emr = patient.emr
    2039                  most_recent_results = {}
    2040
    2041                  # get most recent results for "LOINCs to monitor"
    2042                  loincs2monitor = set()
    2043                  loincs2monitor_data = {}
    2044                  loinc_max_age = {}
    2045                  loinc_max_age_str = {}
    2046                  for intake in self._grid_substances.get_row_data():
    2047                          for l in intake['loincs']:
    2048                                  loincs2monitor.add(l['loinc'])
    2049                                  loincs2monitor_data[l['loinc']] = {
    2050                                          'substance': intake['substance'],
    2051                                          'comment': l['comment']
    2052                                  }
    2053                                  if l['max_age_in_secs'] is not None:
    2054                                          try:
    2055                                                  if loinc_max_age[l['loinc']] > l['max_age_in_secs']:
    2056                                                          loinc_max_age[l['loinc']] = l['max_age_in_secs']
    2057                                                          loinc_max_age_str[l['loinc']] = l['max_age_str']
    2058                                          except KeyError:
    2059                                                  loinc_max_age[l['loinc']] = l['max_age_in_secs']
    2060                                                  loinc_max_age_str[l['loinc']] = l['max_age_str']
    2061                  loincs2monitor_missing = loincs2monitor.copy()
    2062                  for loinc in loincs2monitor:
    2063                          results = emr.get_most_recent_results_in_loinc_group(loincs = [loinc], max_no_of_results = 1)
    2064                          if len(results) == 0:
    2065                                  continue
    2066                          loincs2monitor_missing.remove(loinc)
    2067                          # make unique
    2068                          result = results[0]
    2069                          most_recent_results[result['pk_test_result']] = result
    2070
    2071                  # get most recent results for "general medication monitoring lab panel"
    2072                  if self.__lab_panel is not None:
    2073                          for result in self.__lab_panel.get_most_recent_results (
    2074                                  pk_patient = patient.ID,
    2075                                  order_by = 'unified_abbrev',
    2076                                  group_by_meta_type = True
    2077                          ):
    2078                                  try: loincs2monitor_missing.remove(result['loinc_tt'])
    2079                                  except KeyError: pass
    2080                                  try: loincs2monitor_missing.remove(result['loinc_meta'])
    2081                                  except KeyError: pass
    2082                                  # make unique
    2083                                  most_recent_results[result['pk_test_result']] = result
    2084
    2085                  # those need special treatment
    2086                  gfrs = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
    2087                  gfr = gfrs[0] if len(gfrs) > 0 else None
    2088                  creas = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_creatinine_quantity, max_no_of_results = 1)
    2089                  crea = creas[0] if len(creas) > 0 else None
    2090                  edc = emr.EDC
    2091
    2092                  # display EDC
    2093                  if edc is not None:
    2094                          if emr.EDC_is_fishy:
    2095                                  lbl = wx.StaticText(self, -1, _('EDC (!?!):'))
    2096                                  val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
    2097                          else:
    2098                                  lbl = wx.StaticText(self, -1, _('EDC:'))
    2099                                  val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
    2100                          lbl.SetForegroundColour('blue')
    2101                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2102                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2103                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2104                          self._GSZR_lab.Add(szr)
    2105
    2106                  # decide which among Crea or GFR to show
    2107                  if crea is None:
    2108                          gfr_3_months_older_than_crea = False
    2109                          if gfr is not None:
    2110                                  most_recent_results = [gfr] + most_recent_results
    2111                  elif gfr is None:
    2112                          gfr_3_months_older_than_crea = True
    2113                  else:
    2114                          three_months = pydt.timedelta(weeks = 14)
    2115                          gfr_3_months_older_than_crea = (crea['clin_when'] - gfr['clin_when']) > three_months
    2116                          if not gfr_3_months_older_than_crea:
    2117                                  most_recent_results = [gfr] + most_recent_results
    2118
    2119                  # if GFR not found in most_recent_results or old, then calculate
    2120                  now = gmDateTime.pydt_now_here()
    2121                  if gfr_3_months_older_than_crea:
    2122                          calc = gmClinicalCalculator.cClinicalCalculator()
    2123                          calc.patient = patient
    2124                          gfr = calc.eGFR
    2125                          if gfr.numeric_value is None:
    2126                                  gfr_msg = '?'
    2127                          else:
    2128                                  gfr_msg = _('%.1f (%s ago)') % (
    2129                                          gfr.numeric_value,
    2130                                          gmDateTime.format_interval_medically(now - gfr.date_valid)
    2131                                  )
    2132                          lbl = wx.StaticText(self, -1, _('eGFR:'))
    2133                          lbl.SetForegroundColour('blue')
    2134                          val = wx.StaticText(self, -1, gfr_msg)
    2135                          tts = []
    2136                          for egfr in calc.eGFRs:
    2137                                  if egfr.numeric_value is None:
    2138                                          continue
    2139                                  tts.append(egfr.format (
    2140                                          left_margin = 0,
    2141                                          width = 50,
    2142                                          eol = '\n',
    2143                                          with_formula = False,
    2144                                          with_warnings = True,
    2145                                          with_variables = False,
    2146                                          with_sub_results = False,
    2147                                          return_list = False
    2148                                  ))
    2149                          val.SetToolTip('\n'.join(tts))
    2150                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2151                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2152                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2153                          self._GSZR_lab.Add(szr)
    2154
    2155                  # eventually add most-recent results from monitoring panel and substances monitoring
    2156                  for pk_result in most_recent_results:
    2157                          result = most_recent_results[pk_result]
    2158                          # test type
    2159                          lbl = wx.StaticText(self, -1, '%s:' % result['unified_abbrev'])
    2160                          lbl.SetForegroundColour('blue')
    2161                          # calculate test result
    2162                          indicate_attention = False
    2163                          if result.is_considered_abnormal:
    2164                                  indicate_attention = True
    2165                          # calculate tooltip data
    2166                          max_age = None
    2167                          try:
    2168                                  max_age = loinc_max_age[result['loinc_tt']]
    2169                                  max_age_str = loinc_max_age_str[result['loinc_tt']]
    2170                          except KeyError:
    2171                                  try:
    2172                                          max_age = loinc_max_age[result['loinc_meta']]
    2173                                          max_age_str = loinc_max_age_str[result['loinc_meta']]
    2174                                  except KeyError:
    2175                                          pass
    2176                          subst2monitor = None
    2177                          try:
    2178                                  subst2monitor = loincs2monitor_data[result['loinc_tt']]['substance']
    2179                          except KeyError:
    2180                                  try:
    2181                                          subst2monitor = loincs2monitor_data[result['loinc_meta']]['substance']
    2182                                  except KeyError:
    2183                                          pass
    2184                          monitor_comment = None
    2185                          try:
    2186                                  monitor_comment = loincs2monitor_data[result['loinc_tt']]['comment']
    2187                          except KeyError:
    2188                                  try:
    2189                                          monitor_comment = loincs2monitor_data[result['loinc_meta']]['comment']
    2190                                  except KeyError:
    2191                                          pass
    2192                          result_age = now - result['clin_when']
    2193                          unhappy_reasons = []
    2194                          if result.is_considered_abnormal:
    2195                                  indicator = result.formatted_abnormality_indicator
    2196                                  if indicator == '':
    2197                                          unhappy_reasons.append(_(' - abnormal'))
    2198                                  else:
    2199                                          unhappy_reasons.append(_(' - abnormal: %s') % indicator)
    2200                          if max_age is not None:
    2201                                  if result_age.total_seconds() > max_age:
    2202                                          unhappy_reasons.append(_(' - too old: %s ago (max: %s)') % (
    2203                                                  gmDateTime.format_interval_medically(result_age),
    2204                                                  max_age_str
    2205                                          ))
    2206                          # generate tooltip
    2207                          tt = [_('Most recent: %s ago') % gmDateTime.format_interval_medically(result_age)]
    2208                          if subst2monitor is not None:
    2209                                  tt.append(_('Why monitor: %s') % subst2monitor)
    2210                          if monitor_comment is not None:
    2211                                  tt.append(' %s' % monitor_comment)
    2212                          if len(unhappy_reasons) > 0:
    2213                                  indicate_attention = True
    2214                                  tt.append(_('Problems:'))
    2215                                  tt.extend(unhappy_reasons)
    2216                          tt = '%s\n\n%s' % (
    2217                                  '\n'.join(tt),
    2218                                  result.format()
    2219                          )
    2220                          # set test result and tooltip
    2221                          val = wx.StaticText(self, -1, '%s%s%s' % (
    2222                                  result['unified_val'],
    2223                                  gmTools.coalesce(result['val_unit'], '', ' %s'),
    2224                                  gmTools.bool2subst(indicate_attention, gmTools.u_frowning_face, '', '')
    2225                          ))
    2226                          val.SetToolTip(tt)
    2227                          if result.is_considered_abnormal:
    2228                                  val.SetForegroundColour('red')
    2229                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2230                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2231                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2232                          self._GSZR_lab.Add(szr)
    2233
    2234                  # hint at missing, but required results (set to be
    2235                  # monitored under intakes based on LOINCs):
    2236                  for loinc in loincs2monitor_missing:
    2237                          #szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2238                          loinc_data = gmLOINC.loinc2data(loinc)
    2239                          if loinc_data is None:
    2240                                  loinc_str = loinc
    2241                          else:
    2242                                  loinc_str = loinc_data['term']
    2243                          val = wx.StaticText(self, -1, '%s!' % loinc_str)
    2244                          tt = [
    2245                                  _('No test result for: %s (%s)') % (loinc_str, loinc),
    2246                                  '',
    2247                                  _('Why monitor: %s' % loincs2monitor_data[loinc]['substance'])
    2248                          ]
    2249                          try:
    2250                                  tt.append(' %s' % loincs2monitor_data[loinc]['comment'])
    2251                          except KeyError:
    2252                                  pass
    2253                          val.SetToolTip('\n'.join(tt))
    2254                          val.SetForegroundColour('orange')
    2255                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2256                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2257                          self._GSZR_lab.Add(szr)
    2258
    2259                  self._HLINE_lab.Show()
    2260                  self.Layout()
    2261
    2262          #--------------------------------------------------------
    2263 -        def __refresh_gfr(self, patient):
    2264                  gfrs = patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
    2265                  if len(gfrs) == 0:
    2266                          calc = gmClinicalCalculator.cClinicalCalculator()
    2267                          calc.patient = patient
    2268                          gfr = calc.eGFR
    2269                          if gfr.numeric_value is None:
    2270                                  msg = _('GFR: ?')
    2271                                  tt = gfr.message
    2272                          else:
    2273                                  msg = _('eGFR: %.1f (%s)') % (
    2274                                          gfr.numeric_value,
    2275                                          gmDateTime.pydt_strftime (
    2276                                                  gfr.date_valid,
    2277                                                  format = '%b %Y'
    2278                                          )
    2279                                  )
    2280                                  egfrs = calc.eGFRs
    2281                                  tts = []
    2282                                  for egfr in egfrs:
    2283                                          if egfr.numeric_value is None:
    2284                                                  continue
    2285                                          tts.append(egfr.format (
    2286                                                  left_margin = 0,
    2287                                                  width = 50,
    2288                                                  eol = '\n',
    2289                                                  with_formula = False,
    2290                                                  with_warnings = True,
    2291                                                  with_variables = False,
    2292                                                  with_sub_results = False,
    2293                                                  return_list = False
    2294                                          ))
    2295                                  tt = '\n'.join(tts)
    2296                  else:
    2297                          gfr = gfrs[0]
    2298                          msg = '%s: %s %s (%s)\n' % (
    2299                                  gfr['unified_abbrev'],
    2300                                  gfr['unified_val'],
    2301                                  gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
    2302                                  gmDateTime.pydt_strftime (
    2303                                          gfr['clin_when'],
    2304                                          format = '%b %Y'
    2305                                  )
    2306                          )
    2307                          tt = _('GFR reported by path lab')
    2308
    2309                  self._LBL_gfr.SetLabel(msg)
    2310                  self._LBL_gfr.SetToolTip(tt)
    2311                  self._LBL_gfr.Refresh()
    2312                  self.Layout()
    2313
    2314          #--------------------------------------------------------
    2315 -        def __clear_gfr(self):
    2316                  self._LBL_gfr.SetLabel(_('GFR: ?'))
    2317                  self._LBL_gfr.Refresh()
    2318                  self.Layout()
    2319
    2320          #--------------------------------------------------------
    2321          # event handling
    2322          #--------------------------------------------------------
    2323 -        def __register_interests(self):
    2324                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
    2325                  gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
    2326                  gmDispatcher.connect(signal = 'clin.substance_intake_mod_db', receiver = self._schedule_data_reget)
    2327                  gmDispatcher.connect(signal = 'clin.test_result_mod_db', receiver = self._on_test_result_mod)
    2328
    2329          #--------------------------------------------------------
    2330 -        def _on_test_result_mod(self):
    2331                  self.__refresh_lab(patient = self._grid_substances.patient)
    2332
    2333          #--------------------------------------------------------
    2334 -        def _on_pre_patient_unselection(self):
    2335                  dbcfg = gmCfg.cCfgSQL()
    2336                  pk_panel = dbcfg.get2 (
    2337                          option = 'horstspace.medications_plugin.lab_panel',
    2338                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    2339                          bias = 'user'
    2340                  )
    2341                  if pk_panel is None:
    2342                          self.__lab_panel = None
    2343                  else:
    2344                          self.__lab_panel = gmPathLab.cTestPanel(aPK_obj = pk_panel)
    2345                  self._grid_substances.patient = None
    2346                  self.__refresh_lab(patient = None)
    2347          #--------------------------------------------------------
    2348 -        def _on_post_patient_selection(self):
    2349                  self._schedule_data_reget()
    2350          #--------------------------------------------------------
    2351 -        def _on_add_button_pressed(self, event):
    2352                  self._grid_substances.add_substance()
    2353          #--------------------------------------------------------
    2354 -        def _on_edit_button_pressed(self, event):
    2355                  self._grid_substances.edit_substance()
    2356          #--------------------------------------------------------
    2357 -        def _on_delete_button_pressed(self, event):
    2358                  self._grid_substances.delete_intake()
    2359          #--------------------------------------------------------
    2360 -        def _on_info_button_pressed(self, event):
    2361                  self._grid_substances.show_info_on_entry()
    2362          #--------------------------------------------------------
    2363 -        def _on_interactions_button_pressed(self, event):
    2364                  self._grid_substances.check_interactions()
    2365          #--------------------------------------------------------
    2366 -        def _on_grouping_selected(self, event):
    2367                  event.Skip()
    2368                  selected_item_idx = self._CHCE_grouping.GetSelection()
    2369                  if selected_item_idx is wx.NOT_FOUND:
    2370                          return
    2371                  self._grid_substances.grouping_mode = self._CHCE_grouping.GetClientData(selected_item_idx)
    2372          #--------------------------------------------------------
    2373 -        def _on_show_unapproved_checked(self, event):
    2374                  self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
    2375          #--------------------------------------------------------
    2376 -        def _on_show_inactive_checked(self, event):
    2377                  self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
    2378          #--------------------------------------------------------
    2379 -        def _on_print_button_pressed(self, event):
    2380                  self._grid_substances.print_medication_list()
    2381          #--------------------------------------------------------
    2382 -        def _on_allergy_button_pressed(self, event):
    2383                  self._grid_substances.create_allergy_from_substance()
    2384          #--------------------------------------------------------
    2385 -        def _on_button_kidneys_pressed(self, event):
    2386                  self._grid_substances.show_renal_insufficiency_info()
    2387          #--------------------------------------------------------
    2388 -        def _on_button_heart_pressed(self, event):
    2389                  self._grid_substances.show_cardiac_info()
    2390          #--------------------------------------------------------
    2391 -        def _on_adr_button_pressed(self, event):
    2392                  self._grid_substances.report_ADR()
    2393          #--------------------------------------------------------
    2394 -        def _on_rx_button_pressed(self, event):
    2395                  self._grid_substances.prescribe()
    2396
    2397  #============================================================
    2398  # main
    2399  #------------------------------------------------------------
    2400  if __name__ == '__main__':
    2401
    2402          if len(sys.argv) < 2:
    2403                  sys.exit()
    2404
    2405          if sys.argv[1] != 'test':
    2406                  sys.exit()
    2407
    2408          from Gnumed.business import gmPersonSearch
    2409
    2410          pat = gmPersonSearch.ask_for_patient()
    2411          if pat is None:
    2412                  sys.exit()
    2413          gmPerson.set_active_patient(patient = pat)
    2414
    2415          #----------------------------------------
    2416          app = wx.PyWidgetTester(size = (600, 300))
    2417          app.SetWidget(cSubstanceIntakeObjectPhraseWheel, -1)
    2418          app.MainLoop()
    2419          #manage_substance_intakes()
    2420