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                  for row, col in self.get_selected_cells():
    1392                          rows[row] = True
    1393                  return list(rows)
    1394
    1395          #------------------------------------------------------------
    1396 -        def get_selected_data(self):
    1397                  return [ self.__row_data[row] for row in self.get_selected_rows() ]
    1398
    1399          #------------------------------------------------------------
    1400 -        def get_row_data(self):
    1401                  return self.__row_data.values()
    1402
    1403          #------------------------------------------------------------
    1404 -        def repopulate_grid(self):
    1405
    1406                  self.empty_grid()
    1407
    1408                  if self.__patient is None:
    1409                          return
    1410
    1411                  emr = self.__patient.emr
    1412                  meds = emr.get_current_medications (
    1413                          order_by = self.__grouping2order_by_clauses[self.__grouping_mode],
    1414                          include_unapproved = self.__filter_show_unapproved,
    1415                          include_inactive = self.__filter_show_inactive
    1416                  )
    1417                  if not meds:
    1418                          return
    1419
    1420                  self.BeginBatch()
    1421
    1422                  # columns
    1423                  labels = self.__grouping2col_labels[self.__grouping_mode]
    1424                  if self.__filter_show_unapproved:
    1425                          self.AppendCols(numCols = len(labels) + 1)
    1426                  else:
    1427                          self.AppendCols(numCols = len(labels))
    1428                  for col_idx in range(len(labels)):
    1429                          self.SetColLabelValue(col_idx, labels[col_idx])
    1430                  if self.__filter_show_unapproved:
    1431                          #self.SetColLabelValue(len(labels), u'OK?')
    1432                          self.SetColLabelValue(len(labels), '')
    1433                          self.SetColSize(len(labels), 40)
    1434
    1435                  self.AppendRows(numRows = len(meds))
    1436
    1437                  # loop over data
    1438                  for row_idx in range(len(meds)):
    1439                          med = meds[row_idx]
    1440                          self.__row_data[row_idx] = med
    1441
    1442                          if med['is_currently_active'] is True:
    1443                                  atcs = []
    1444                                  if med['atc_substance'] is not None:
    1445                                          atcs.append(med['atc_substance'])
    1446                                  allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (med['substance'],))
    1447                                  if allg not in [None, False]:
    1448                                          attr = self.GetOrCreateCellAttr(row_idx, 0)
    1449                                          if allg['type'] == 'allergy':
    1450                                                  attr.SetTextColour('red')
    1451                                          else:
    1452                                                  #attr.SetTextColour('yellow')           # too light
    1453                                                  #attr.SetTextColour('pink')                     # too light
    1454                                                  #attr.SetTextColour('dark orange')      # slightly better
    1455                                                  attr.SetTextColour('magenta')
    1456                                          self.SetRowAttr(row_idx, attr)
    1457                          else:
    1458                                  attr = self.GetOrCreateCellAttr(row_idx, 0)
    1459                                  attr.SetTextColour('grey')
    1460                                  self.SetRowAttr(row_idx, attr)
    1461
    1462                          if self.__grouping_mode in ['episode', 'start']:
    1463                                  if med['pk_episode'] is None:
    1464                                          self.__prev_cell_0 = None
    1465                                          epi = gmTools.u_diameter
    1466                                  else:
    1467                                          if self.__prev_cell_0 == med['episode']:
    1468                                                  epi = ''
    1469                                          else:
    1470                                                  self.__prev_cell_0 = med['episode']
    1471                                                  epi = gmTools.coalesce(med['episode'], '')
    1472                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = epi, width = 40))
    1473
    1474                                  self.SetCellValue(row_idx, 1, med['substance'])
    1475                                  self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
    1476                                  self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
    1477                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1478
    1479                                  if med['pk_drug_product'] is None:
    1480                                          product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
    1481                                  else:
    1482                                          if med['is_fake_product']:
    1483                                                  product = '%s (%s)' % (
    1484                                                          gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1485                                                          med['l10n_preparation']
    1486                                                  )
    1487                                          else:
    1488                                                  product = '%s (%s)' % (
    1489                                                          gmTools.coalesce(med['product'], ''),
    1490                                                          med['l10n_preparation']
    1491                                                  )
    1492                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
    1493
    1494                          elif self.__grouping_mode == 'issue':
    1495                                  if med['pk_health_issue'] is None:
    1496                                          self.__prev_cell_0 = None
    1497                                          issue = '%s%s' % (
    1498                                                  gmTools.u_diameter,
    1499                                                  gmTools.coalesce(med['episode'], '', ' (%s)')
    1500                                          )
    1501                                  else:
    1502                                          if self.__prev_cell_0 == med['health_issue']:
    1503                                                  issue = ''
    1504                                          else:
    1505                                                  self.__prev_cell_0 = med['health_issue']
    1506                                                  issue = med['health_issue']
    1507                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = issue, width = 40))
    1508
    1509                                  self.SetCellValue(row_idx, 1, med['substance'])
    1510                                  self.SetCellValue(row_idx, 2, '%s %s' % (med['amount'], med['unit']))
    1511                                  self.SetCellValue(row_idx, 3, gmTools.coalesce(med['schedule'], ''))
    1512                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1513
    1514                                  if med['pk_drug_product'] is None:
    1515                                          product = '%s (%s)' % (gmTools.u_diameter, med['l10n_preparation'])
    1516                                  else:
    1517                                          if med['is_fake_product']:
    1518                                                  product = '%s (%s)' % (
    1519                                                          gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1520                                                          med['l10n_preparation']
    1521                                                  )
    1522                                          else:
    1523                                                  product = '%s (%s)' % (
    1524                                                          gmTools.coalesce(med['product'], ''),
    1525                                                          med['l10n_preparation']
    1526                                                  )
    1527                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = product, width = 35))
    1528
    1529                          elif self.__grouping_mode == 'product':
    1530
    1531                                  if med['pk_drug_product'] is None:
    1532                                          self.__prev_cell_0 = None
    1533                                          product =  '%s (%s)' % (
    1534                                                  gmTools.u_diameter,
    1535                                                  med['l10n_preparation']
    1536                                          )
    1537                                  else:
    1538                                          if self.__prev_cell_0 == med['product']:
    1539                                                  product = ''
    1540                                          else:
    1541                                                  self.__prev_cell_0 = med['product']
    1542                                                  if med['is_fake_product']:
    1543                                                          product = '%s (%s)' % (
    1544                                                                  gmTools.coalesce(med['product'], '', _('%s <fake>')),
    1545                                                                  med['l10n_preparation']
    1546                                                          )
    1547                                                  else:
    1548                                                          product = '%s (%s)' % (
    1549                                                                  gmTools.coalesce(med['product'], ''),
    1550                                                                  med['l10n_preparation']
    1551                                                          )
    1552                                  self.SetCellValue(row_idx, 0, gmTools.wrap(text = product, width = 35))
    1553
    1554                                  self.SetCellValue(row_idx, 1, gmTools.coalesce(med['schedule'], ''))
    1555                                  self.SetCellValue(row_idx, 2, med['substance'])
    1556                                  self.SetCellValue(row_idx, 3, '%s %s' % (med['amount'], med['unit']))
    1557                                  self.SetCellValue(row_idx, 4, med.medically_formatted_start_end)
    1558
    1559                                  if med['pk_health_issue'] is None:
    1560                                          issue = '%s%s' % (
    1561                                                  gmTools.u_diameter,
    1562                                                  gmTools.coalesce(med['episode'], '', ' (%s)')
    1563                                          )
    1564                                  else:
    1565                                          issue = gmTools.coalesce(med['health_issue'], '')
    1566                                  self.SetCellValue(row_idx, 5, gmTools.wrap(text = issue, width = 40))
    1567
    1568                          else:
    1569                                  raise ValueError('unknown grouping mode [%s]' % self.__grouping_mode)
    1570
    1571                          if med['notes'] is not None:
    1572                                  self.SetCellValue(row_idx, 6, gmTools.wrap(text = med['notes'], width = 50))
    1573
    1574                          if self.__filter_show_unapproved:
    1575                                  self.SetCellValue (
    1576                                          row_idx,
    1577                                          len(labels),
    1578                                          gmTools.bool2subst(med['intake_is_approved_of'], gmTools.u_checkmark_thin, gmTools.u_frowning_face, '?')
    1579                                  )
    1580                                  font = self.GetCellFont(row_idx, len(labels))
    1581                                  font.SetPointSize(font.GetPointSize() + 2)
    1582                                  self.SetCellFont(row_idx, len(labels), font)
    1583
    1584                          #self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
    1585
    1586                  self.AutoSize()
    1587                  self.EndBatch()
    1588          #------------------------------------------------------------
    1589 -        def empty_grid(self):
    1590                  self.BeginBatch()
    1591                  self.ClearGrid()
    1592                  # Windows cannot do "nothing", it rather decides to assert()
    1593                  # on thinking it is supposed to do nothing
    1594                  if self.GetNumberRows() > 0:
    1595                          self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
    1596                  if self.GetNumberCols() > 0:
    1597                          self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
    1598                  self.EndBatch()
    1599                  self.__row_data = {}
    1600                  self.__prev_cell_0 = None
    1601
    1602          #------------------------------------------------------------
    1603 -        def show_info_on_entry(self):
    1604
    1605                  if len(self.__row_data) == 0:
    1606                          return
    1607
    1608                  sel_rows = self.get_selected_rows()
    1609                  if len(sel_rows) != 1:
    1610                          return
    1611
    1612                  drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
    1613                  if drug_db is None:
    1614                          return
    1615
    1616                  intake = self.get_selected_data()[0]            # just in case
    1617                  if intake['product'] is None:
    1618                          drug_db.show_info_on_substance(substance_intake = intake)
    1619                  else:
    1620                          drug_db.show_info_on_drug(substance_intake = intake)
    1621
    1622          #------------------------------------------------------------
    1623 -        def show_renal_insufficiency_info(self):
    1624                  search_term = None
    1625                  if len(self.__row_data) > 0:
    1626                          sel_rows = self.get_selected_rows()
    1627                          if len(sel_rows) == 1:
    1628                                  search_term = self.get_selected_data()[0]
    1629                  gmNetworkTools.open_url_in_browser(url = gmMedication.drug2renal_insufficiency_url(search_term = search_term))
    1630
    1631          #------------------------------------------------------------
    1632 -        def show_cardiac_info(self):
    1633                  gmNetworkTools.open_url_in_browser(url = gmMedication.URL_long_qt)
    1634
    1635          #------------------------------------------------------------
    1636 -        def report_ADR(self):
    1637                  dbcfg = gmCfg.cCfgSQL()
    1638                  url = dbcfg.get2 (
    1639                          option = 'external.urls.report_ADR',
    1640                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1641                          bias = 'user',
    1642                          default = gmMedication.URL_drug_adr_german_default
    1643                  )
    1644                  gmNetworkTools.open_url_in_browser(url = url)
    1645
    1646          #------------------------------------------------------------
    1647 -        def prescribe(self):
    1648                  prescribe_drugs (
    1649                          parent = self,
    1650                          emr = self.__patient.emr
    1651                  )
    1652          #------------------------------------------------------------
    1653 -        def check_interactions(self):
    1654
    1655                  if len(self.__row_data) == 0:
    1656                          return
    1657
    1658                  drug_db = gmSubstanceMgmtWidgets.get_drug_database(patient = self.__patient)
    1659                  if drug_db is None:
    1660                          return
    1661
    1662                  if len(self.get_selected_rows()) > 1:
    1663                          drug_db.check_interactions(substance_intakes = self.get_selected_data())
    1664                  else:
    1665                          drug_db.check_interactions(substance_intakes = self.__row_data.values())
    1666          #------------------------------------------------------------
    1667 -        def add_substance(self):
    1668                  edit_intake_of_substance(parent = self, substance = None)
    1669          #------------------------------------------------------------
    1670 -        def edit_substance(self):
    1671
    1672                  rows = self.get_selected_rows()
    1673
    1674                  if len(rows) == 0:
    1675                          return
    1676
    1677                  if len(rows) > 1:
    1678                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit more than one substance at once.'), beep = True)
    1679                          return
    1680
    1681                  subst = self.get_selected_data()[0]
    1682                  edit_intake_of_substance(parent = self, substance = subst)
    1683
    1684          #------------------------------------------------------------
    1685 -        def delete_intake(self):
    1686
    1687                  rows = self.get_selected_rows()
    1688
    1689                  if len(rows) == 0:
    1690                          return
    1691
    1692                  if len(rows) > 1:
    1693                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete more than one substance at once.'), beep = True)
    1694                          return
    1695
    1696                  intake = self.get_selected_data()[0]
    1697                  delete_substance_intake(parent = self, intake = intake)
    1698
    1699          #------------------------------------------------------------
    1700 -        def create_allergy_from_substance(self):
    1701                  rows = self.get_selected_rows()
    1702
    1703                  if len(rows) == 0:
    1704                          return
    1705
    1706                  if len(rows) > 1:
    1707                          gmDispatcher.send(signal = 'statustext', msg = _('Cannot create allergy from more than one substance at once.'), beep = True)
    1708                          return
    1709
    1710                  return turn_substance_intake_into_allergy (
    1711                          parent = self,
    1712                          intake = self.get_selected_data()[0],
    1713                          emr = self.__patient.emr
    1714                  )
    1715          #------------------------------------------------------------
    1716 -        def print_medication_list(self):
    1717                  # there could be some filtering/user interaction going on here
    1718                  print_medication_list(parent = self)
    1719          #------------------------------------------------------------
    1720 -        def get_row_tooltip(self, row=None):
    1721
    1722                  try:
    1723                          entry = self.__row_data[row]
    1724                  except KeyError:
    1725                          return ' '
    1726
    1727                  emr = self.__patient.emr
    1728                  atcs = []
    1729                  if entry['atc_substance'] is not None:
    1730                          atcs.append(entry['atc_substance'])
    1731  #               if entry['atc_drug'] is not None:
    1732  #                       atcs.append(entry['atc_drug'])
    1733  #               allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],), product_name = entry['product'])
    1734                  allg = emr.is_allergic_to(atcs = tuple(atcs), inns = (entry['substance'],))
    1735
    1736                  tt = _('Substance intake entry (%s, %s)   [#%s]                     \n') % (
    1737                          gmTools.bool2subst (
    1738                                  boolean = entry['is_currently_active'],
    1739                                  true_return = gmTools.bool2subst (
    1740                                          boolean = entry['seems_inactive'],
    1741                                          true_return = _('active, needs check'),
    1742                                          false_return = _('active'),
    1743                                          none_return = _('assumed active')
    1744                                  ),
    1745                                  false_return = _('inactive')
    1746                          ),
    1747                          gmTools.bool2subst (
    1748                                  boolean = entry['intake_is_approved_of'],
    1749                                  true_return = _('approved'),
    1750                                  false_return = _('unapproved')
    1751                          ),
    1752                          entry['pk_substance_intake']
    1753                  )
    1754
    1755                  if allg not in [None, False]:
    1756                          certainty = gmTools.bool2subst(allg['definite'], _('definite'), _('suspected'))
    1757                          tt += '\n'
    1758                          tt += ' !! ---- Cave ---- !!\n'
    1759                          tt += ' %s (%s): %s (%s)\n' % (
    1760                                  allg['l10n_type'],
    1761                                  certainty,
    1762                                  allg['descriptor'],
    1763                                  gmTools.coalesce(allg['reaction'], '')[:40]
    1764                          )
    1765                          tt += '\n'
    1766
    1767                  tt += ' ' + _('Substance: %s   [#%s]\n') % (entry['substance'], entry['pk_substance'])
    1768                  tt += ' ' + _('Preparation: %s\n') % entry['l10n_preparation']
    1769                  tt += ' ' + _('Amount per dose: %s %s') % (entry['amount'], entry['unit'])
    1770                  tt += '\n'
    1771                  tt += gmTools.coalesce(entry['atc_substance'], '', _(' ATC (substance): %s\n'))
    1772
    1773                  tt += '\n'
    1774
    1775                  tt += gmTools.coalesce (
    1776                          entry['product'],
    1777                          '',
    1778                          _(' Product name: %%s   [#%s]\n') % entry['pk_drug_product']
    1779                  )
    1780                  tt += gmTools.coalesce(entry['atc_drug'], '', _(' ATC (drug): %s\n'))
    1781
    1782                  tt += '\n'
    1783
    1784                  tt += gmTools.coalesce(entry['schedule'], '', _(' Regimen: %s\n'))
    1785
    1786                  if entry['is_long_term']:
    1787                          duration = ' %s %s' % (gmTools.u_arrow2right, gmTools.u_infinity)
    1788                  else:
    1789                          if entry['duration'] is None:
    1790                                  duration = ''
    1791                          else:
    1792                                  duration = ' %s %s' % (gmTools.u_arrow2right, gmDateTime.format_interval(entry['duration'], gmDateTime.acc_days))
    1793
    1794                  tt += _(' Started %s%s%s\n') % (
    1795                          entry.medically_formatted_start,
    1796                          duration,
    1797                          gmTools.bool2subst(entry['is_long_term'], _(' (long-term)'), _(' (short-term)'), '')
    1798                  )
    1799
    1800                  if entry['discontinued'] is not None:
    1801                          tt += _(' Discontinued %s\n') % gmDateTime.pydt_strftime(entry['discontinued'], '%Y %b %d')
    1802                          tt += gmTools.coalesce(entry['discontinue_reason'], '', _(' Reason: %s\n'))
    1803
    1804                  tt += '\n'
    1805
    1806                  tt += gmTools.coalesce(entry['aim'], '', _(' Aim: %s\n'))
    1807                  tt += gmTools.coalesce(entry['episode'], '', _(' Episode: %s\n'))
    1808                  tt += gmTools.coalesce(entry['health_issue'], '', _(' Health issue: %s\n'))
    1809                  tt += gmTools.coalesce(entry['notes'], '', _(' Advice: %s\n'))
    1810
    1811                  tt += '\n'
    1812
    1813                  tt += _('Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % ({
    1814                          'row_ver': entry['row_version'],
    1815                          'mod_when': gmDateTime.pydt_strftime(entry['modified_when'], '%Y %b %d  %H:%M:%S'),
    1816                          'mod_by': entry['modified_by']
    1817                  })
    1818
    1819                  return tt
    1820
    1821          #------------------------------------------------------------
    1822          # internal helpers
    1823          #------------------------------------------------------------
    1824 -        def __init_ui(self):
    1825                  self.CreateGrid(0, 1)
    1826                  self.EnableEditing(0)
    1827                  self.EnableDragGridSize(1)
    1828                  self.SetSelectionMode(wx.grid.Grid.wxGridSelectRows)
    1829
    1830                  self.SetColLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTER)
    1831
    1832                  self.SetRowLabelSize(0)
    1833                  self.SetRowLabelAlignment(horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
    1834
    1835          #------------------------------------------------------------
    1836          # properties
    1837          #------------------------------------------------------------
    1838 -        def _get_patient(self):
    1839                  return self.__patient
    1840
    1841 -        def _set_patient(self, patient):
    1842                  self.__patient = patient
    1843                  self.repopulate_grid()
    1844
    1845          patient = property(_get_patient, _set_patient)
    1846          #------------------------------------------------------------
    1847 -        def _get_grouping_mode(self):
    1848                  return self.__grouping_mode
    1849
    1850 -        def _set_grouping_mode(self, mode):
    1851                  self.__grouping_mode = mode
    1852                  self.repopulate_grid()
    1853
    1854          grouping_mode = property(_get_grouping_mode, _set_grouping_mode)
    1855          #------------------------------------------------------------
    1856 -        def _get_filter_show_unapproved(self):
    1857                  return self.__filter_show_unapproved
    1858
    1859 -        def _set_filter_show_unapproved(self, val):
    1860                  self.__filter_show_unapproved = val
    1861                  self.repopulate_grid()
    1862
    1863          filter_show_unapproved = property(_get_filter_show_unapproved, _set_filter_show_unapproved)
    1864          #------------------------------------------------------------
    1865 -        def _get_filter_show_inactive(self):
    1866                  return self.__filter_show_inactive
    1867
    1868 -        def _set_filter_show_inactive(self, val):
    1869                  self.__filter_show_inactive = val
    1870                  self.repopulate_grid()
    1871
    1872          filter_show_inactive = property(_get_filter_show_inactive, _set_filter_show_inactive)
    1873          #------------------------------------------------------------
    1874          # event handling
    1875          #------------------------------------------------------------
    1876 -        def __register_events(self):
    1877                  # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
    1878                  self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
    1879                  #self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
    1880                  #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
    1881
    1882                  # editing cells
    1883                  self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
    1884          #------------------------------------------------------------
    1885 -        def __on_mouse_over_cells(self, evt):
    1886                  """Calculate where the mouse is and set the tooltip dynamically."""
    1887
    1888                  # Use CalcUnscrolledPosition() to get the mouse position within the
    1889                  # entire grid including what's offscreen
    1890                  x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
    1891
    1892                  # use this logic to prevent tooltips outside the actual cells
    1893                  # apply to GetRowSize, too
    1894  #        tot = 0
    1895  #        for col in range(self.NumberCols):
    1896  #            tot += self.GetColSize(col)
    1897  #            if xpos <= tot:
    1898  #                self.tool_tip.Tip = 'Tool tip for Column %s' % (
    1899  #                    self.GetColLabelValue(col))
    1900  #                break
    1901  #            else:  # mouse is in label area beyond the right-most column
    1902  #            self.tool_tip.Tip = ''
    1903
    1904                  row, col = self.XYToCell(x, y)
    1905
    1906                  if row == self.__prev_tooltip_row:
    1907                          return
    1908
    1909                  self.__prev_tooltip_row = row
    1910
    1911                  try:
    1912                          evt.GetEventObject().SetToolTip(self.get_row_tooltip(row = row))
    1913                  except KeyError:
    1914                          pass
    1915          #------------------------------------------------------------
    1916 -        def __on_cell_left_dclicked(self, evt):
    1917                  row = evt.GetRow()
    1918                  data = self.__row_data[row]
    1919                  edit_intake_of_substance(parent = self, substance = data)
    1920
    1921  #============================================================
    1922 -def configure_default_medications_lab_panel(parent=None):
    1923
    1924          panels = gmPathLab.get_test_panels(order_by = 'description')
    1925          gmCfgWidgets.configure_string_from_list_option (
    1926                  parent = parent,
    1927                  message = _(
    1928                          '\n'
    1929                          'Select the measurements panel to show in the medications plugin.'
    1930                          '\n'
    1931                  ),
    1932                  option = 'horstspace.medications_plugin.lab_panel',
    1933                  bias = 'user',
    1934                  default_value = None,
    1935                  choices = [ '%s%s' % (p['description'], gmTools.coalesce(p['comment'], '', ' (%s)')) for p in panels ],
    1936                  columns = [_('Measurements panel')],
    1937                  data = [ p['pk_test_panel'] for p in panels ],
    1938                  caption = _('Configuring medications plugin measurements panel')
    1939          )
    1940
    1941  #============================================================
    1942 -def configure_adr_url():
    1943
    1944          def is_valid(value):
    1945                  value = value.strip()
    1946                  if value == '':
    1947                          return True, gmMedication.URL_drug_adr_german_default
    1948                  try:
    1949                          urllib.request.urlopen(value)
    1950                          return True, value
    1951                  except Exception:
    1952                          return True, value
    1953
    1954          gmCfgWidgets.configure_string_option (
    1955                  message = _(
    1956                          'GNUmed will use this URL to access a website which lets\n'
    1957                          'you report an adverse drug reaction (ADR).\n'
    1958                          '\n'
    1959                          'If you leave this empty it will fall back\n'
    1960                          'to an URL for reporting ADRs in Germany.'
    1961                  ),
    1962                  option = 'external.urls.report_ADR',
    1963                  bias = 'user',
    1964                  default_value = gmMedication.URL_drug_adr_german_default,
    1965                  validator = is_valid
    1966          )
    1967
    1968  #============================================================
    1969  from Gnumed.wxGladeWidgets import wxgCurrentSubstancesPnl
    1970
    1971 -class cCurrentSubstancesPnl(wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl, gmRegetMixin.cRegetOnPaintMixin):
    1972
    1973          """Panel holding a grid with current substances. Used as notebook page."""
    1974
    1975 -        def __init__(self, *args, **kwargs):
    1976
    1977                  wxgCurrentSubstancesPnl.wxgCurrentSubstancesPnl.__init__(self, *args, **kwargs)
    1978                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1979
    1980                  self.__grouping_choice_labels = [
    1981                          {'label': _('Health issue'), 'data': 'issue'} ,
    1982                          {'label': _('Drug product'), 'data': 'product'},
    1983                          {'label': _('Episode'), 'data': 'episode'},
    1984                          {'label': _('Started'), 'data': 'start'}
    1985                  ]
    1986                  self.__lab_panel = None
    1987
    1988                  self.__init_ui()
    1989                  self.__register_interests()
    1990
    1991          #-----------------------------------------------------
    1992 -        def __init_ui(self):
    1993                  self._CHCE_grouping.Clear()
    1994                  for option in self.__grouping_choice_labels:
    1995                          self._CHCE_grouping.Append(option['label'], option['data'])
    1996                  self._CHCE_grouping.SetSelection(0)
    1997
    1998                  tt = self._BTN_heart.GetToolTipText()
    1999                  try:
    2000                          self._BTN_heart.SetToolTip(tt % gmMedication.URL_long_qt)
    2001                  except TypeError:
    2002                          _log.exception('translation error: %s', tt)
    2003
    2004                  tt = self._BTN_kidneys.GetToolTipText()
    2005                  try:
    2006                          self._BTN_kidneys.SetToolTip(tt % gmMedication.URL_renal_insufficiency)
    2007                  except TypeError:
    2008                          _log.exception('translation error: %s', tt)
    2009
    2010          #-----------------------------------------------------
    2011          # reget-on-paint mixin API
    2012          #-----------------------------------------------------
    2013 -        def _populate_with_data(self):
    2014                  """Populate cells with data from model."""
    2015                  pat = gmPerson.gmCurrentPatient()
    2016                  if pat.connected:
    2017                          self._grid_substances.patient = pat
    2018                          self.__refresh_gfr(pat)
    2019                          self.__refresh_lab(patient = pat)
    2020                  else:
    2021                          self._grid_substances.patient = None
    2022                          self.__clear_gfr()
    2023                          self.__refresh_lab(patient = None)
    2024                  return True
    2025
    2026          #--------------------------------------------------------
    2027 -        def __refresh_lab(self, patient):
    2028
    2029                  self._GSZR_lab.Clear(True)              # also delete child windows
    2030                  self._HLINE_lab.Hide()
    2031
    2032                  if patient is None:
    2033                          self.Layout()
    2034                          return
    2035
    2036                  emr = patient.emr
    2037                  most_recent_results = {}
    2038
    2039                  # get most recent results for "LOINCs to monitor"
    2040                  loincs2monitor = set()
    2041                  loincs2monitor_data = {}
    2042                  loinc_max_age = {}
    2043                  loinc_max_age_str = {}
    2044                  for intake in self._grid_substances.get_row_data():
    2045                          for l in intake['loincs']:
    2046                                  loincs2monitor.add(l['loinc'])
    2047                                  loincs2monitor_data[l['loinc']] = {
    2048                                          'substance': intake['substance'],
    2049                                          'comment': l['comment']
    2050                                  }
    2051                                  if l['max_age_in_secs'] is not None:
    2052                                          try:
    2053                                                  if loinc_max_age[l['loinc']] > l['max_age_in_secs']:
    2054                                                          loinc_max_age[l['loinc']] = l['max_age_in_secs']
    2055                                                          loinc_max_age_str[l['loinc']] = l['max_age_str']
    2056                                          except KeyError:
    2057                                                  loinc_max_age[l['loinc']] = l['max_age_in_secs']
    2058                                                  loinc_max_age_str[l['loinc']] = l['max_age_str']
    2059                  loincs2monitor_missing = loincs2monitor.copy()
    2060                  for loinc in loincs2monitor:
    2061                          results = emr.get_most_recent_results_in_loinc_group(loincs = [loinc], max_no_of_results = 1)
    2062                          if len(results) == 0:
    2063                                  continue
    2064                          loincs2monitor_missing.remove(loinc)
    2065                          # make unique
    2066                          result = results[0]
    2067                          most_recent_results[result['pk_test_result']] = result
    2068
    2069                  # get most recent results for "general medication monitoring lab panel"
    2070                  if self.__lab_panel is not None:
    2071                          for result in self.__lab_panel.get_most_recent_results (
    2072                                  pk_patient = patient.ID,
    2073                                  order_by = 'unified_abbrev',
    2074                                  group_by_meta_type = True
    2075                          ):
    2076                                  try: loincs2monitor_missing.remove(result['loinc_tt'])
    2077                                  except KeyError: pass
    2078                                  try: loincs2monitor_missing.remove(result['loinc_meta'])
    2079                                  except KeyError: pass
    2080                                  # make unique
    2081                                  most_recent_results[result['pk_test_result']] = result
    2082
    2083                  # those need special treatment
    2084                  gfrs = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
    2085                  gfr = gfrs[0] if len(gfrs) > 0 else None
    2086                  creas = emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_creatinine_quantity, max_no_of_results = 1)
    2087                  crea = creas[0] if len(creas) > 0 else None
    2088                  edc = emr.EDC
    2089
    2090                  # display EDC
    2091                  if edc is not None:
    2092                          if emr.EDC_is_fishy:
    2093                                  lbl = wx.StaticText(self, -1, _('EDC (!?!):'))
    2094                                  val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
    2095                          else:
    2096                                  lbl = wx.StaticText(self, -1, _('EDC:'))
    2097                                  val = wx.StaticText(self, -1, gmDateTime.pydt_strftime(edc, format = '%Y %b %d'))
    2098                          lbl.SetForegroundColour('blue')
    2099                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2100                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2101                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2102                          self._GSZR_lab.Add(szr)
    2103
    2104                  # decide which among Crea or GFR to show
    2105                  if crea is None:
    2106                          gfr_3_months_older_than_crea = False
    2107                          if gfr is not None:
    2108                                  most_recent_results = [gfr] + most_recent_results
    2109                  elif gfr is None:
    2110                          gfr_3_months_older_than_crea = True
    2111                  else:
    2112                          three_months = pydt.timedelta(weeks = 14)
    2113                          gfr_3_months_older_than_crea = (crea['clin_when'] - gfr['clin_when']) > three_months
    2114                          if not gfr_3_months_older_than_crea:
    2115                                  most_recent_results = [gfr] + most_recent_results
    2116
    2117                  # if GFR not found in most_recent_results or old, then calculate
    2118                  now = gmDateTime.pydt_now_here()
    2119                  if gfr_3_months_older_than_crea:
    2120                          calc = gmClinicalCalculator.cClinicalCalculator()
    2121                          calc.patient = patient
    2122                          gfr = calc.eGFR
    2123                          if gfr.numeric_value is None:
    2124                                  gfr_msg = '?'
    2125                          else:
    2126                                  gfr_msg = _('%.1f (%s ago)') % (
    2127                                          gfr.numeric_value,
    2128                                          gmDateTime.format_interval_medically(now - gfr.date_valid)
    2129                                  )
    2130                          lbl = wx.StaticText(self, -1, _('eGFR:'))
    2131                          lbl.SetForegroundColour('blue')
    2132                          val = wx.StaticText(self, -1, gfr_msg)
    2133                          tts = []
    2134                          for egfr in calc.eGFRs:
    2135                                  if egfr.numeric_value is None:
    2136                                          continue
    2137                                  tts.append(egfr.format (
    2138                                          left_margin = 0,
    2139                                          width = 50,
    2140                                          eol = '\n',
    2141                                          with_formula = False,
    2142                                          with_warnings = True,
    2143                                          with_variables = False,
    2144                                          with_sub_results = False,
    2145                                          return_list = False
    2146                                  ))
    2147                          val.SetToolTip('\n'.join(tts))
    2148                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2149                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2150                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2151                          self._GSZR_lab.Add(szr)
    2152
    2153                  # eventually add most-recent results from monitoring panel and substances monitoring
    2154                  for pk_result in most_recent_results:
    2155                          result = most_recent_results[pk_result]
    2156                          # test type
    2157                          lbl = wx.StaticText(self, -1, '%s:' % result['unified_abbrev'])
    2158                          lbl.SetForegroundColour('blue')
    2159                          # calculate test result
    2160                          indicate_attention = False
    2161                          if result.is_considered_abnormal:
    2162                                  indicate_attention = True
    2163                          # calculate tooltip data
    2164                          max_age = None
    2165                          try:
    2166                                  max_age = loinc_max_age[result['loinc_tt']]
    2167                                  max_age_str = loinc_max_age_str[result['loinc_tt']]
    2168                          except KeyError:
    2169                                  try:
    2170                                          max_age = loinc_max_age[result['loinc_meta']]
    2171                                          max_age_str = loinc_max_age_str[result['loinc_meta']]
    2172                                  except KeyError:
    2173                                          pass
    2174                          subst2monitor = None
    2175                          try:
    2176                                  subst2monitor = loincs2monitor_data[result['loinc_tt']]['substance']
    2177                          except KeyError:
    2178                                  try:
    2179                                          subst2monitor = loincs2monitor_data[result['loinc_meta']]['substance']
    2180                                  except KeyError:
    2181                                          pass
    2182                          monitor_comment = None
    2183                          try:
    2184                                  monitor_comment = loincs2monitor_data[result['loinc_tt']]['comment']
    2185                          except KeyError:
    2186                                  try:
    2187                                          monitor_comment = loincs2monitor_data[result['loinc_meta']]['comment']
    2188                                  except KeyError:
    2189                                          pass
    2190                          result_age = now - result['clin_when']
    2191                          unhappy_reasons = []
    2192                          if result.is_considered_abnormal:
    2193                                  indicator = result.formatted_abnormality_indicator
    2194                                  if indicator == '':
    2195                                          unhappy_reasons.append(_(' - abnormal'))
    2196                                  else:
    2197                                          unhappy_reasons.append(_(' - abnormal: %s') % indicator)
    2198                          if max_age is not None:
    2199                                  if result_age.total_seconds() > max_age:
    2200                                          unhappy_reasons.append(_(' - too old: %s ago (max: %s)') % (
    2201                                                  gmDateTime.format_interval_medically(result_age),
    2202                                                  max_age_str
    2203                                          ))
    2204                          # generate tooltip
    2205                          tt = [_('Most recent: %s ago') % gmDateTime.format_interval_medically(result_age)]
    2206                          if subst2monitor is not None:
    2207                                  tt.append(_('Why monitor: %s') % subst2monitor)
    2208                          if monitor_comment is not None:
    2209                                  tt.append(' %s' % monitor_comment)
    2210                          if len(unhappy_reasons) > 0:
    2211                                  indicate_attention = True
    2212                                  tt.append(_('Problems:'))
    2213                                  tt.extend(unhappy_reasons)
    2214                          tt = '%s\n\n%s' % (
    2215                                  '\n'.join(tt),
    2216                                  result.format()
    2217                          )
    2218                          # set test result and tooltip
    2219                          val = wx.StaticText(self, -1, '%s%s%s' % (
    2220                                  result['unified_val'],
    2221                                  gmTools.coalesce(result['val_unit'], '', ' %s'),
    2222                                  gmTools.bool2subst(indicate_attention, gmTools.u_frowning_face, '', '')
    2223                          ))
    2224                          val.SetToolTip(tt)
    2225                          if result.is_considered_abnormal:
    2226                                  val.SetForegroundColour('red')
    2227                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2228                          szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2229                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2230                          self._GSZR_lab.Add(szr)
    2231
    2232                  # hint at missing, but required results (set to be
    2233                  # monitored under intakes based on LOINCs):
    2234                  for loinc in loincs2monitor_missing:
    2235                          #szr.Add(lbl, 0, wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3)
    2236                          loinc_data = gmLOINC.loinc2data(loinc)
    2237                          if loinc_data is None:
    2238                                  loinc_str = loinc
    2239                          else:
    2240                                  loinc_str = loinc_data['term']
    2241                          val = wx.StaticText(self, -1, '%s!' % loinc_str)
    2242                          tt = [
    2243                                  _('No test result for: %s (%s)') % (loinc_str, loinc),
    2244                                  '',
    2245                                  _('Why monitor: %s' % loincs2monitor_data[loinc]['substance'])
    2246                          ]
    2247                          try:
    2248                                  tt.append(' %s' % loincs2monitor_data[loinc]['comment'])
    2249                          except KeyError:
    2250                                  pass
    2251                          val.SetToolTip('\n'.join(tt))
    2252                          val.SetForegroundColour('orange')
    2253                          szr = wx.BoxSizer(wx.HORIZONTAL)
    2254                          szr.Add(val, 1, wx.ALIGN_CENTER_VERTICAL)
    2255                          self._GSZR_lab.Add(szr)
    2256
    2257                  self._HLINE_lab.Show()
    2258                  self.Layout()
    2259
    2260          #--------------------------------------------------------
    2261 -        def __refresh_gfr(self, patient):
    2262                  gfrs = patient.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_gfr_quantity, max_no_of_results = 1)
    2263                  if len(gfrs) == 0:
    2264                          calc = gmClinicalCalculator.cClinicalCalculator()
    2265                          calc.patient = patient
    2266                          gfr = calc.eGFR
    2267                          if gfr.numeric_value is None:
    2268                                  msg = _('GFR: ?')
    2269                                  tt = gfr.message
    2270                          else:
    2271                                  msg = _('eGFR: %.1f (%s)') % (
    2272                                          gfr.numeric_value,
    2273                                          gmDateTime.pydt_strftime (
    2274                                                  gfr.date_valid,
    2275                                                  format = '%b %Y'
    2276                                          )
    2277                                  )
    2278                                  egfrs = calc.eGFRs
    2279                                  tts = []
    2280                                  for egfr in egfrs:
    2281                                          if egfr.numeric_value is None:
    2282                                                  continue
    2283                                          tts.append(egfr.format (
    2284                                                  left_margin = 0,
    2285                                                  width = 50,
    2286                                                  eol = '\n',
    2287                                                  with_formula = False,
    2288                                                  with_warnings = True,
    2289                                                  with_variables = False,
    2290                                                  with_sub_results = False,
    2291                                                  return_list = False
    2292                                          ))
    2293                                  tt = '\n'.join(tts)
    2294                  else:
    2295                          gfr = gfrs[0]
    2296                          msg = '%s: %s %s (%s)\n' % (
    2297                                  gfr['unified_abbrev'],
    2298                                  gfr['unified_val'],
    2299                                  gmTools.coalesce(gfr['abnormality_indicator'], '', ' (%s)'),
    2300                                  gmDateTime.pydt_strftime (
    2301                                          gfr['clin_when'],
    2302                                          format = '%b %Y'
    2303                                  )
    2304                          )
    2305                          tt = _('GFR reported by path lab')
    2306
    2307                  self._LBL_gfr.SetLabel(msg)
    2308                  self._LBL_gfr.SetToolTip(tt)
    2309                  self._LBL_gfr.Refresh()
    2310                  self.Layout()
    2311
    2312          #--------------------------------------------------------
    2313 -        def __clear_gfr(self):
    2314                  self._LBL_gfr.SetLabel(_('GFR: ?'))
    2315                  self._LBL_gfr.Refresh()
    2316                  self.Layout()
    2317
    2318          #--------------------------------------------------------
    2319          # event handling
    2320          #--------------------------------------------------------
    2321 -        def __register_interests(self):
    2322                  gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
    2323                  gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
    2324                  gmDispatcher.connect(signal = 'clin.substance_intake_mod_db', receiver = self._schedule_data_reget)
    2325                  gmDispatcher.connect(signal = 'clin.test_result_mod_db', receiver = self._on_test_result_mod)
    2326
    2327          #--------------------------------------------------------
    2328 -        def _on_test_result_mod(self):
    2329                  self.__refresh_lab(patient = self._grid_substances.patient)
    2330
    2331          #--------------------------------------------------------
    2332 -        def _on_pre_patient_unselection(self):
    2333                  dbcfg = gmCfg.cCfgSQL()
    2334                  pk_panel = dbcfg.get2 (
    2335                          option = 'horstspace.medications_plugin.lab_panel',
    2336                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    2337                          bias = 'user'
    2338                  )
    2339                  if pk_panel is None:
    2340                          self.__lab_panel = None
    2341                  else:
    2342                          self.__lab_panel = gmPathLab.cTestPanel(aPK_obj = pk_panel)
    2343                  self._grid_substances.patient = None
    2344                  self.__refresh_lab(patient = None)
    2345          #--------------------------------------------------------
    2346 -        def _on_post_patient_selection(self):
    2347                  self._schedule_data_reget()
    2348          #--------------------------------------------------------
    2349 -        def _on_add_button_pressed(self, event):
    2350                  self._grid_substances.add_substance()
    2351          #--------------------------------------------------------
    2352 -        def _on_edit_button_pressed(self, event):
    2353                  self._grid_substances.edit_substance()
    2354          #--------------------------------------------------------
    2355 -        def _on_delete_button_pressed(self, event):
    2356                  self._grid_substances.delete_intake()
    2357          #--------------------------------------------------------
    2358 -        def _on_info_button_pressed(self, event):
    2359                  self._grid_substances.show_info_on_entry()
    2360          #--------------------------------------------------------
    2361 -        def _on_interactions_button_pressed(self, event):
    2362                  self._grid_substances.check_interactions()
    2363          #--------------------------------------------------------
    2364 -        def _on_grouping_selected(self, event):
    2365                  event.Skip()
    2366                  selected_item_idx = self._CHCE_grouping.GetSelection()
    2367                  if selected_item_idx is wx.NOT_FOUND:
    2368                          return
    2369                  self._grid_substances.grouping_mode = self._CHCE_grouping.GetClientData(selected_item_idx)
    2370          #--------------------------------------------------------
    2371 -        def _on_show_unapproved_checked(self, event):
    2372                  self._grid_substances.filter_show_unapproved = self._CHBOX_show_unapproved.GetValue()
    2373          #--------------------------------------------------------
    2374 -        def _on_show_inactive_checked(self, event):
    2375                  self._grid_substances.filter_show_inactive = self._CHBOX_show_inactive.GetValue()
    2376          #--------------------------------------------------------
    2377 -        def _on_print_button_pressed(self, event):
    2378                  self._grid_substances.print_medication_list()
    2379          #--------------------------------------------------------
    2380 -        def _on_allergy_button_pressed(self, event):
    2381                  self._grid_substances.create_allergy_from_substance()
    2382          #--------------------------------------------------------
    2383 -        def _on_button_kidneys_pressed(self, event):
    2384                  self._grid_substances.show_renal_insufficiency_info()
    2385          #--------------------------------------------------------
    2386 -        def _on_button_heart_pressed(self, event):
    2387                  self._grid_substances.show_cardiac_info()
    2388          #--------------------------------------------------------
    2389 -        def _on_adr_button_pressed(self, event):
    2390                  self._grid_substances.report_ADR()
    2391          #--------------------------------------------------------
    2392 -        def _on_rx_button_pressed(self, event):
    2393                  self._grid_substances.prescribe()
    2394
    2395  #============================================================
    2396  # main
    2397  #------------------------------------------------------------
    2398  if __name__ == '__main__':
    2399
    2400          if len(sys.argv) < 2:
    2401                  sys.exit()
    2402
    2403          if sys.argv[1] != 'test':
    2404                  sys.exit()
    2405
    2406          from Gnumed.business import gmPersonSearch
    2407
    2408          pat = gmPersonSearch.ask_for_patient()
    2409          if pat is None:
    2410                  sys.exit()
    2411          gmPerson.set_active_patient(patient = pat)
    2412
    2413          #----------------------------------------
    2414          app = wx.PyWidgetTester(size = (600, 300))
    2415          app.SetWidget(cSubstanceIntakeObjectPhraseWheel, -1)
    2416          app.MainLoop()
    2417          #manage_substance_intakes()
    2418