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