Gnumed.wxpython.gmVaccWidgets

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

# Source Code for Module Gnumed.wxpython.gmVaccWidgets

       1  """GNUmed immunisation/vaccination widgets.
       2
       3  Modelled after Richard Terry's design document.
       4
       5  copyright: authors
       6  """
       7  #======================================================================
       8  __author__ = "R.Terry, S.J.Tan, K.Hilbert"
       9  __license__ = "GPL v2 or later (details at http://www.gnu.org)"
      10
      11  import sys
      12  import logging
      13
      14
      15  import wx
      16
      17
      18  if __name__ == '__main__':
      19          sys.path.insert(0, '../../')
      20  from Gnumed.pycommon import gmDispatcher
      21  from Gnumed.pycommon import gmMatchProvider
      22  from Gnumed.pycommon import gmTools
      23  from Gnumed.pycommon import gmI18N
      24  from Gnumed.pycommon import gmCfg
      25  from Gnumed.pycommon import gmCfg2
      26  from Gnumed.pycommon import gmDateTime
      27  from Gnumed.pycommon import gmNetworkTools
      28  from Gnumed.pycommon import gmPrinting
      29  from Gnumed.pycommon import gmPG2
      30
      31  from Gnumed.business import gmPerson
      32  from Gnumed.business import gmVaccination
      33  from Gnumed.business import gmPraxis
      34  from Gnumed.business import gmProviderInbox
      35
      36  from Gnumed.wxpython import gmPhraseWheel
      37  from Gnumed.wxpython import gmTerryGuiParts
      38  from Gnumed.wxpython import gmRegetMixin
      39  from Gnumed.wxpython import gmGuiHelpers
      40  from Gnumed.wxpython import gmEditArea
      41  from Gnumed.wxpython import gmListWidgets
      42  from Gnumed.wxpython import gmFormWidgets
      43  from Gnumed.wxpython import gmMacro
      44  from Gnumed.wxpython import gmAuthWidgets
      45  from Gnumed.wxpython import gmSubstanceMgmtWidgets
      46
      47
      48  _log = logging.getLogger('gm.vacc')
      49
      50  #======================================================================
      51  # vaccine related widgets
      52  #----------------------------------------------------------------------
      53 -def regenerate_generic_vaccines():
      54
      55          dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Regenerating generic vaccines'))
      56          if dbo_conn is None:
      57                  return False
      58
      59          wx.BeginBusyCursor()
      60          _cfg = gmCfg2.gmCfgData()
      61          sql_script = gmVaccination.write_generic_vaccine_sql (
      62                  'client-%s' % _cfg.get(option = 'client_version'),
      63                  include_indications_mapping = False
      64          )
      65          _log.debug('regenerating generic vaccines, SQL script: %s', sql_script)
      66          if not gmPG2.run_sql_script(sql_script, conn = dbo_conn):
      67                  wx.EndBusyCursor()
      68                  gmGuiHelpers.gm_show_warning (
      69                          aMessage = _('Error regenerating generic vaccines.\n\nSee [%s]') % sql_script,
      70                          aTitle = _('Regenerating generic vaccines')
      71                  )
      72                  return False
      73
      74          gmDispatcher.send(signal = 'statustext', msg = _('Successfully regenerated generic vaccines ...'), beep = False)
      75          wx.EndBusyCursor()
      76          return True
      77
      78  #----------------------------------------------------------------------
      79 -def edit_vaccine(parent=None, vaccine=None, single_entry=True):
      80          ea = cVaccineEAPnl(parent, -1)
      81          ea.data = vaccine
      82          ea.mode = gmTools.coalesce(vaccine, 'new', 'edit')
      83          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
      84          dlg.SetTitle(gmTools.coalesce(vaccine, _('Adding new vaccine'), _('Editing vaccine')))
      85          if dlg.ShowModal() == wx.ID_OK:
      86                  dlg.Destroy()
      87                  return True
      88          dlg.Destroy()
      89          return False
      90
      91  #----------------------------------------------------------------------
      92 -def manage_vaccines(parent=None):
      93
      94          if parent is None:
      95                  parent = wx.GetApp().GetTopWindow()
      96
      97  #       #------------------------------------------------------------
      98  #       def delete(vaccine=None):
      99  #               product = vaccine.product
     100  #               deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
     101  #               if not deleted:
     102  #                       gmGuiHelpers.gm_show_info (
     103  #                               _(      'Cannot delete vaccine\n'
     104  #                                       '\n'
     105  #                                       ' %s - %s (#%s)\n'
     106  #                                       '\n'
     107  #                                       'It is probably documented in a vaccination.'
     108  #                               ) % (
     109  #                                       vaccine['vaccine'],
     110  #                                       vaccine['l10n_preparation'],
     111  #                                       vaccine['pk_vaccine']
     112  #                               ),
     113  #                               _('Deleting vaccine')
     114  #                       )
     115  #                       return False
     116  #               delete_product = gmGuiHelpers.gm_show_question (
     117  #                       title = _('Deleting vaccine'),
     118  #                       question = _(
     119  #                               u'Fully delete the vaccine (including the associated drug product) ?\n'
     120  #                               u'\n'
     121  #                               u' "%s" (%s)'
     122  #                       ) % (product['product'], product['l10n_preparation'])
     123  #               )
     124  #               if delete_product:
     125  #                       pass
     126  #               return True
     127
     128          #------------------------------------------------------------
     129          def manage_drug_products(vaccine):
     130                  gmSubstanceMgmtWidgets.manage_drug_products(parent = parent)
     131                  return True
     132
     133          #------------------------------------------------------------
     134          def edit(vaccine=None):
     135                  return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
     136
     137          #------------------------------------------------------------
     138          def get_tooltip(vaccine):
     139                  if vaccine is None:
     140                          return None
     141                  return '\n'.join(vaccine.format())
     142
     143          #------------------------------------------------------------
     144          def refresh(lctrl):
     145                  vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
     146
     147                  items = [ [
     148                          '%s' % v['pk_drug_product'],
     149                          '%s%s' % (
     150                                  v['vaccine'],
     151                                  gmTools.bool2subst (
     152                                          v['is_fake_vaccine'],
     153                                          ' (%s)' % _('fake'),
     154                                          ''
     155                                  )
     156                          ),
     157                          v['l10n_preparation'],
     158                          gmTools.coalesce(v['atc_code'], ''),
     159                          '%s - %s' % (
     160                                  gmTools.coalesce(v['min_age'], '?'),
     161                                  gmTools.coalesce(v['max_age'], '?'),
     162                          ),
     163                          gmTools.coalesce(v['comment'], '')
     164                  ] for v in vaccines ]
     165                  lctrl.set_string_items(items)
     166                  lctrl.set_data(vaccines)
     167
     168          #------------------------------------------------------------
     169          gmListWidgets.get_choices_from_list (
     170                  parent = parent,
     171                  caption = _('Showing vaccine details'),
     172                  columns = [ '#', _('Vaccine'), _('Preparation'), _('ATC'), _('Age range'), _('Comment') ],
     173                  single_selection = True,
     174                  refresh_callback = refresh,
     175                  edit_callback = edit,
     176                  new_callback = edit,
     177                  #delete_callback = delete,
     178                  list_tooltip_callback = get_tooltip,
     179                  left_extra_button = (_('Products'), _('Manage drug products'), manage_drug_products)
     180          )
     181
     182  #----------------------------------------------------------------------
     183 -class cBatchNoPhraseWheel(gmPhraseWheel.cPhraseWheel):
     184
     185 -        def __init__(self, *args, **kwargs):
     186
     187                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     188
     189                  context = {
     190                          'ctxt_vaccine': {
     191                                  'where_part': 'AND pk_vaccine = %(pk_vaccine)s',
     192                                  'placeholder': 'pk_vaccine'
     193                          }
     194                  }
     195
     196                  query = """
     197  SELECT data, field_label, list_label FROM (
     198
     199          SELECT distinct on (field_label)
     200                  data,
     201                  field_label,
     202                  list_label,
     203                  rank
     204          FROM ((
     205                          -- batch_no by vaccine
     206                          SELECT
     207                                  batch_no AS data,
     208                                  batch_no AS field_label,
     209                                  batch_no || ' (' || vaccine || ')' AS list_label,
     210                                  1 as rank
     211                          FROM
     212                                  clin.v_vaccinations
     213                          WHERE
     214                                  batch_no %(fragment_condition)s
     215                                  %(ctxt_vaccine)s
     216                  ) UNION ALL (
     217                          -- batch_no for any vaccine
     218                          SELECT
     219                                  batch_no AS data,
     220                                  batch_no AS field_label,
     221                                  batch_no || ' (' || vaccine || ')' AS list_label,
     222                                  2 AS rank
     223                          FROM
     224                                  clin.v_vaccinations
     225                          WHERE
     226                                  batch_no %(fragment_condition)s
     227                  )
     228
     229          ) AS matching_batch_nos
     230
     231  ) as unique_matches
     232
     233  ORDER BY rank, list_label
     234  LIMIT 25
     235  """
     236                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
     237                  mp.setThresholds(1, 2, 3)
     238                  self.matcher = mp
     239
     240                  self.unset_context(context = 'pk_vaccine')
     241                  self.SetToolTip(_('Enter or select the batch/lot number of the vaccine used.'))
     242                  self.selection_only = False
     243
     244  #----------------------------------------------------------------------
     245 -class cVaccinePhraseWheel(gmPhraseWheel.cPhraseWheel):
     246
     247 -        def __init__(self, *args, **kwargs):
     248
     249                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     250
     251                  # consider ATCs in ref.drug_product and ref.vacc_indication
     252                  query = """
     253  SELECT data, list_label, field_label FROM (
     254
     255          SELECT DISTINCT ON (data)
     256                  data,
     257                  list_label,
     258                  field_label
     259          FROM ((
     260                          -- fragment -> vaccine
     261                          SELECT
     262                                  r_v_v.pk_vaccine
     263                                          AS data,
     264                                  r_v_v.vaccine || ' ('
     265                                  ||      (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
     266                                                  SELECT unnest(r_v_v.indications)->>'l10n_indication' AS ind_desc
     267                                          ) AS l10n_inds)
     268                                  || ')'
     269                                          AS list_label,
     270                                  r_v_v.vaccine
     271                                          AS field_label
     272                          FROM
     273                                  ref.v_vaccines r_v_v
     274                          WHERE
     275                                  r_v_v.vaccine %(fragment_condition)s
     276
     277                  ) union all (
     278
     279                          -- fragment -> localized indication -> vaccines
     280                          SELECT
     281                                   r_vi4v.pk_vaccine
     282                                          AS data,
     283                                  r_vi4v.vaccine || ' ('
     284                                  ||      (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
     285                                                  SELECT unnest(r_vi4v.indications)->>'l10n_indication' AS ind_desc
     286                                          ) AS l10n_inds)
     287                                  || ')'
     288                                          AS list_label,
     289                                  r_vi4v.vaccine
     290                                          AS field_label
     291                          FROM
     292                                  ref.v_indications4vaccine r_vi4v
     293                          WHERE
     294                                   r_vi4v.l10n_indication %(fragment_condition)s
     295
     296                  ) union all (
     297
     298                          -- fragment -> indication -> vaccines
     299                          SELECT
     300                                  r_vi4v.pk_vaccine
     301                                          AS data,
     302                                  r_vi4v.vaccine || ' ('
     303                                  ||      (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
     304                                                  SELECT unnest(r_vi4v.indications)->>'l10n_indication' AS ind_desc
     305                                          ) AS l10n_inds)
     306                                  || ')'
     307                                          AS list_label,
     308                                  r_vi4v.vaccine
     309                                          AS field_label
     310                          FROM
     311                                  ref.v_indications4vaccine r_vi4v
     312                          WHERE
     313                                  r_vi4v.indication %(fragment_condition)s
     314                  )
     315          ) AS distinct_total
     316
     317  ) AS total
     318
     319  ORDER by list_label
     320  LIMIT 25
     321  """
     322                  mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
     323                  mp.setThresholds(1, 2, 3)
     324                  self.matcher = mp
     325
     326                  self.selection_only = True
     327          #------------------------------------------------------------------
     328 -        def _data2instance(self):
     329                  return gmVaccination.cVaccine(aPK_obj = self.GetData())
     330
     331  #----------------------------------------------------------------------
     332  from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
     333
     334 -class cVaccineEAPnl(wxgVaccineEAPnl.wxgVaccineEAPnl, gmEditArea.cGenericEditAreaMixin):
     335
     336 -        def __init__(self, *args, **kwargs):
     337                  try:
     338                          data = kwargs['vaccine']
     339                          del kwargs['vaccine']
     340                  except KeyError:
     341                          data = None
     342
     343                  wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs)
     344                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     345
     346                  self.mode = 'new'
     347                  self.data = data
     348                  if data is not None:
     349                          self.mode = 'edit'
     350
     351                  self.__indications = None
     352
     353          #----------------------------------------------------------------
     354 -        def __refresh_indications(self):
     355                  self._TCTRL_indications.SetValue('')
     356                  if self.data is None:
     357                          return
     358                  self._TCTRL_indications.SetValue('- ' + '\n- '.join([ i['l10n_indication'] for i in self.data['indications'] ]))
     359
     360          #----------------------------------------------------------------
     361          # generic Edit Area mixin API
     362          #----------------------------------------------------------------
     363 -        def _valid_for_save(self):
     364
     365                  has_errors = False
     366
     367                  if self._PRW_drug_product.GetValue().strip() == '':
     368                          has_errors = True
     369                          self._PRW_drug_product.display_as_valid(False)
     370                  else:
     371                          self._PRW_drug_product.display_as_valid(True)
     372
     373                  atc = self._PRW_atc.GetValue().strip()
     374                  if (atc == '') or (atc.startswith('J07')):
     375                          self._PRW_atc.display_as_valid(True)
     376                  else:
     377                          if self._PRW_atc.GetData() is None:
     378                                  self._PRW_atc.display_as_valid(True)
     379                          else:
     380                                  has_errors = True
     381                                  self._PRW_atc.display_as_valid(False)
     382
     383                  val = self._PRW_age_min.GetValue().strip()
     384                  if val == '':
     385                          self._PRW_age_min.display_as_valid(True)
     386                  else:
     387                          if gmDateTime.str2interval(val) is None:
     388                                  has_errors = True
     389                                  self._PRW_age_min.display_as_valid(False)
     390                          else:
     391                                  self._PRW_age_min.display_as_valid(True)
     392
     393                  val = self._PRW_age_max.GetValue().strip()
     394                  if val == '':
     395                          self._PRW_age_max.display_as_valid(True)
     396                  else:
     397                          if gmDateTime.str2interval(val) is None:
     398                                  has_errors = True
     399                                  self._PRW_age_max.display_as_valid(False)
     400                          else:
     401                                  self._PRW_age_max.display_as_valid(True)
     402
     403                  # complex conditions
     404                  # are we editing ?
     405                  if self.mode == 'edit':
     406                          change_of_product = self.data['pk_drug_product'] != self._PRW_drug_product.GetData()
     407                          if change_of_product and self.data.is_in_use:
     408                                  do_it = gmGuiHelpers.gm_show_question (
     409                                          aTitle = _('Saving vaccine'),
     410                                          aMessage = _(
     411                                                  'This vaccine is already in use:\n'
     412                                                  '\n'
     413                                                  ' "%s"\n'
     414                                                  '\n'
     415                                                  'Are you absolutely positively sure that\n'
     416                                                  'you really want to edit this vaccine ?\n'
     417                                                  '\n'
     418                                                  'This will change the vaccine name and/or target\n'
     419                                                  'conditions in each patient this vaccine was\n'
     420                                                  'used in to document a vaccination with.\n'
     421                                          ) % self._PRW_drug_product.GetValue().strip()
     422                                  )
     423                                  if not do_it:
     424                                          has_errors = True
     425                  else:
     426                          if self._PRW_drug_product.GetData() is None:
     427                                  # need to ask for indications ?
     428                                  if self._PRW_drug_product.GetValue().strip() != '':
     429                                          self.__indications = gmSubstanceMgmtWidgets.manage_substance_doses(vaccine_indications_only = True)
     430                                          if self.__indications is None:
     431                                                  has_errors = True
     432                          else:
     433                                  # existing drug product selected
     434                                  pass
     435
     436                  return (has_errors is False)
     437
     438          #----------------------------------------------------------------
     439 -        def _save_as_new(self):
     440
     441                  # save the data as a new instance
     442                  vaccine = gmVaccination.create_vaccine (
     443                          pk_drug_product = self._PRW_drug_product.GetData(),
     444                          product_name = self._PRW_drug_product.GetValue().strip(),
     445                          indications = self.__indications,
     446                          is_live = self._CHBOX_live.GetValue()
     447                  )
     448                  val = self._PRW_age_min.GetValue().strip()
     449                  if val != '':
     450                          vaccine['min_age'] = gmDateTime.str2interval(val)
     451                  val = self._PRW_age_max.GetValue().strip()
     452                  if val != '':
     453                          vaccine['max_age'] = gmDateTime.str2interval(val)
     454                  val = self._TCTRL_comment.GetValue().strip()
     455                  if val != '':
     456                          vaccine['comment'] = val
     457                  vaccine.save()
     458
     459                  drug = vaccine.product
     460                  drug['is_fake_product'] = self._CHBOX_fake.GetValue()
     461                  val = self._PRW_atc.GetData()
     462                  if val is not None:
     463                          if val != 'J07':
     464                                  drug['atc'] = val.strip()
     465                  drug.save()
     466
     467                  # must be done very late or else the property access
     468                  # will refresh the display such that later field
     469                  # access will return empty values
     470                  self.data = vaccine
     471
     472                  return True
     473
     474          #----------------------------------------------------------------
     475 -        def _save_as_update(self):
     476
     477                  drug = self.data.product
     478                  drug['product'] = self._PRW_drug_product.GetValue().strip()
     479                  drug['is_fake_product'] = self._CHBOX_fake.GetValue()
     480                  val = self._PRW_atc.GetData()
     481                  if val is not None:
     482                          if val != 'J07':
     483                                  drug['atc'] = val.strip()
     484                  drug.save()
     485
     486                  self.data['is_live'] = self._CHBOX_live.GetValue()
     487                  val = self._PRW_age_min.GetValue().strip()
     488                  if val != '':
     489                          self.data['min_age'] = gmDateTime.str2interval(val)
     490                  if val != '':
     491                          self.data['max_age'] = gmDateTime.str2interval(val)
     492                  val = self._TCTRL_comment.GetValue().strip()
     493                  if val != '':
     494                          self.data['comment'] = val
     495                  self.data.save()
     496
     497                  return True
     498
     499          #----------------------------------------------------------------
     500 -        def _refresh_as_new(self):
     501                  self._PRW_drug_product.SetText(value = '', data = None, suppress_smarts = True)
     502                  self._CHBOX_live.SetValue(False)
     503                  self._CHBOX_fake.SetValue(False)
     504                  self._PRW_atc.SetText(value = '', data = None, suppress_smarts = True)
     505                  self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
     506                  self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
     507                  self._TCTRL_comment.SetValue('')
     508
     509                  self.__refresh_indications()
     510
     511                  self._PRW_drug_product.SetFocus()
     512
     513          #----------------------------------------------------------------
     514 -        def _refresh_from_existing(self):
     515                  self._PRW_drug_product.SetText(value = self.data['vaccine'], data = self.data['pk_drug_product'])
     516                  self._CHBOX_live.SetValue(self.data['is_live'])
     517                  self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
     518                  self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
     519                  if self.data['min_age'] is None:
     520                          self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
     521                  else:
     522                          self._PRW_age_min.SetText (
     523                                  value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
     524                                  data = self.data['min_age']
     525                          )
     526                  if self.data['max_age'] is None:
     527                          self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
     528                  else:
     529                          self._PRW_age_max.SetText (
     530                                  value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
     531                                  data = self.data['max_age']
     532                          )
     533                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
     534
     535                  self.__refresh_indications()
     536
     537                  self._PRW_drug_product.SetFocus()
     538
     539          #----------------------------------------------------------------
     540 -        def _refresh_as_new_from_existing(self):
     541                  self._refresh_as_new()
     542
     543          #----------------------------------------------------------------
     544          #----------------------------------------------------------------
     545
     546  #======================================================================
     547  # vaccination related widgets
     548  #----------------------------------------------------------------------
     549 -def print_vaccinations(parent=None):
     550
     551          if parent is None:
     552                  parent = wx.GetApp().GetTopWindow()
     553
     554          vaccs_printout = gmFormWidgets.generate_form_from_template (
     555                  parent = parent,
     556                  template_types = [
     557                          'Medical statement',
     558                          'vaccination report',
     559                          'vaccination record',
     560                          'reminder'
     561                  ],
     562                  edit = False
     563          )
     564
     565          if vaccs_printout is None:
     566                  return False
     567
     568          return gmFormWidgets.act_on_generated_forms (
     569                  parent = parent,
     570                  forms = [vaccs_printout],
     571                  jobtype = 'vaccinations',
     572                  episode_name = 'administrative',
     573                  review_copy_as_normal = True
     574          )
     575
     576  #----------------------------------------------------------------------
     577 -def edit_vaccination(parent=None, vaccination=None, single_entry=True):
     578          ea = cVaccinationEAPnl(parent, -1)
     579          ea.data = vaccination
     580          ea.mode = gmTools.coalesce(vaccination, 'new', 'edit')
     581          dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
     582          dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination')))
     583          if dlg.ShowModal() == wx.ID_OK:
     584                  dlg.Destroy()
     585                  return True
     586          dlg.Destroy()
     587          if not single_entry:
     588                  return True
     589          return False
     590
     591  #----------------------------------------------------------------------
     592 -def manage_vaccinations(parent=None, latest_only=False, expand_indications=False):
     593
     594          pat = gmPerson.gmCurrentPatient()
     595          emr = pat.emr
     596
     597          if parent is None:
     598                  parent = wx.GetApp().GetTopWindow()
     599
     600          #------------------------------------------------------------
     601          def browse2schedules(vaccination=None):
     602                  dbcfg = gmCfg.cCfgSQL()
     603                  url = dbcfg.get2 (
     604                          option = 'external.urls.vaccination_plans',
     605                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
     606                          bias = 'user',
     607                          default = gmVaccination.URL_vaccination_plan
     608                  )
     609
     610                  gmNetworkTools.open_url_in_browser(url = url)
     611                  return False
     612
     613          #------------------------------------------------------------
     614          def print_vaccs(vaccination=None):
     615                  print_vaccinations(parent = parent)
     616                  return False
     617
     618          #------------------------------------------------------------
     619          def add_recall(vaccination=None):
     620                  if vaccination is None:
     621                          subject = _('vaccination recall')
     622                  else:
     623                          subject = _('vaccination recall (%s)') % vaccination['vaccine']
     624
     625                  recall = gmProviderInbox.create_inbox_message (
     626                          message_type = _('Vaccination'),
     627                          subject = subject,
     628                          patient = pat.ID,
     629                          staff = None
     630                  )
     631
     632                  if vaccination is not None:
     633                          recall['data'] = _('Existing vaccination:\n\n%s') % '\n'.join(vaccination.format(
     634                                  with_indications = True,
     635                                  with_comment = True,
     636                                  with_reaction = False,
     637                                  date_format = '%Y %b %d'
     638                          ))
     639                          recall.save()
     640
     641                  from Gnumed.wxpython import gmProviderInboxWidgets
     642                  gmProviderInboxWidgets.edit_inbox_message (
     643                          parent = parent,
     644                          message = recall,
     645                          single_entry = False
     646                  )
     647
     648                  return False
     649
     650          #------------------------------------------------------------
     651          def get_tooltip(vaccination):
     652                  if vaccination is None:
     653                          return None
     654                  return '\n'.join(vaccination.format (
     655                          with_indications = True,
     656                          with_comment = True,
     657                          with_reaction = True,
     658                          date_format = '%Y %b %d'
     659                  ))
     660
     661          #------------------------------------------------------------
     662          def edit(vaccination=None):
     663                  return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
     664
     665          #------------------------------------------------------------
     666          def delete(vaccination=None):
     667                  gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
     668                  return True
     669
     670          #------------------------------------------------------------
     671          def refresh(lctrl):
     672
     673                  items = []
     674                  data = []
     675                  if latest_only:
     676                          latest_vaccs = emr.get_latest_vaccinations()
     677                          for indication in sorted(latest_vaccs.keys()):
     678                                  no_of_shots4ind, latest_vacc4ind = latest_vaccs[indication]
     679                                  items.append ([
     680                                          indication,
     681                                          _('%s (latest of %s: %s ago)') % (
     682                                                  gmDateTime.pydt_strftime(latest_vacc4ind['date_given'], format = '%Y %b'),
     683                                                  no_of_shots4ind,
     684                                                  gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
     685                                          ),
     686                                          latest_vacc4ind['vaccine'],
     687                                          latest_vacc4ind['batch_no'],
     688                                          gmTools.coalesce(latest_vacc4ind['site'], ''),
     689                                          gmTools.coalesce(latest_vacc4ind['reaction'], ''),
     690                                          gmTools.coalesce(latest_vacc4ind['comment'], '')
     691                                  ])
     692                                  data.append(latest_vacc4ind)
     693                  else:
     694                          shots = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
     695                          if expand_indications:
     696                                  shots_by_ind = {}
     697                                  for shot in shots:
     698                                          for ind in shot['indications']:
     699                                                  try:
     700                                                          shots_by_ind[ind['l10n_indication']].append(shot)
     701                                                  except KeyError:
     702                                                          shots_by_ind[ind['l10n_indication']] = [shot]
     703                                  for ind in sorted(shots_by_ind.keys()):
     704                                          idx = len(shots_by_ind[ind])
     705                                          for shot in shots_by_ind[ind]:
     706                                                  items.append ([
     707                                                          '%s (#%s)' % (ind, idx),
     708                                                          _('%s (%s ago)') % (
     709                                                                  gmDateTime.pydt_strftime(shot['date_given'], '%Y %b %d'),
     710                                                                  gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - shot['date_given'])
     711                                                          ),
     712                                                          shot['vaccine'],
     713                                                          shot['batch_no'],
     714                                                          gmTools.coalesce(shot['site'], ''),
     715                                                          gmTools.coalesce(shot['reaction'], ''),
     716                                                          gmTools.coalesce(shot['comment'], '')
     717                                                  ])
     718                                                  idx -= 1
     719                                                  data.append(shot)
     720                          else:
     721                                  items = [ [
     722                                          gmDateTime.pydt_strftime(s['date_given'], '%Y %b %d'),
     723                                          s['vaccine'],
     724                                          ', '.join([ i['l10n_indication'] for i in s['indications'] ]),
     725                                          s['batch_no'],
     726                                          gmTools.coalesce(s['site'], ''),
     727                                          gmTools.coalesce(s['reaction'], ''),
     728                                          gmTools.coalesce(s['comment'], '')
     729                                  ] for s in shots ]
     730                                  data = shots
     731
     732                  lctrl.set_string_items(items)
     733                  lctrl.set_data(data)
     734
     735          #------------------------------------------------------------
     736          if latest_only:
     737                  msg = _('Most recent vaccination for each indication.\n')
     738                  cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
     739          else:
     740                  if expand_indications:
     741                          msg = _('Complete vaccination history (per indication).\n')
     742                          cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
     743                  else:
     744                          msg = _('Complete vaccination history (by shot).\n')
     745                          cols = [ _('Date'), _('Vaccine'), _('Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
     746
     747          gmListWidgets.get_choices_from_list (
     748                  parent = parent,
     749                  msg = msg,
     750                  caption = _('Showing vaccinations.'),
     751                  columns = cols,
     752                  single_selection = True,
     753                  refresh_callback = refresh,
     754                  new_callback = edit,
     755                  edit_callback = edit,
     756                  delete_callback = delete,
     757                  list_tooltip_callback = get_tooltip,
     758                  left_extra_button = (_('Print'), _('Print vaccinations or recalls.'), print_vaccs),
     759                  middle_extra_button = (_('Recall'), _('Add a recall for a vaccination'), add_recall),
     760                  right_extra_button = (_('Vx schedules'), _('Open a browser showing vaccination schedules.'), browse2schedules)
     761          )
     762
     763  #----------------------------------------------------------------------
     764  from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
     765
     766 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
     767          """
     768          - warn on apparent duplicates
     769          - ask if "missing" (= previous, non-recorded) vaccinations
     770            should be estimated and saved (add note "auto-generated")
     771
     772          Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
     773          """
     774 -        def __init__(self, *args, **kwargs):
     775
     776                  try:
     777                          data = kwargs['vaccination']
     778                          del kwargs['vaccination']
     779                  except KeyError:
     780                          data = None
     781
     782                  wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs)
     783                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     784
     785                  self.mode = 'new'
     786                  self.data = data
     787                  if data is not None:
     788                          self.mode = 'edit'
     789
     790                  self.__init_ui()
     791
     792          #----------------------------------------------------------------
     793 -        def __init_ui(self):
     794                  # adjust phrasewheels etc
     795                  self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
     796                  self._PRW_provider.selection_only = False
     797                  self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
     798
     799          #----------------------------------------------------------------
     800 -        def _on_PRW_vaccine_lost_focus(self):
     801
     802                  vaccine = self._PRW_vaccine.GetData(as_instance=True)
     803
     804                  if self.mode == 'edit':
     805                          if vaccine is None:
     806                                  self._PRW_batch.unset_context(context = 'pk_vaccine')
     807                          else:
     808                                  self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
     809                  # we are entering a new vaccination
     810                  else:
     811                          if vaccine is None:
     812                                  self._PRW_batch.unset_context(context = 'pk_vaccine')
     813                          else:
     814                                  self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
     815
     816                  self.__refresh_indications()
     817
     818          #----------------------------------------------------------------
     819 -        def _on_PRW_reaction_lost_focus(self):
     820                  if self._PRW_reaction.GetValue().strip() == '':
     821                          self._BTN_report.Enable(False)
     822                  else:
     823                          self._BTN_report.Enable(True)
     824
     825          #----------------------------------------------------------------
     826 -        def __refresh_indications(self):
     827                  self._TCTRL_indications.SetValue('')
     828                  vaccine = self._PRW_vaccine.GetData(as_instance = True)
     829                  if vaccine is None:
     830                          return
     831                  lines = []
     832                  emr = gmPerson.gmCurrentPatient().emr
     833                  latest_vaccs = emr.get_latest_vaccinations (
     834                          atc_indications = [ i['atc_indication'] for i in vaccine['indications'] ]
     835                  )
     836                  for l10n_ind in [ i['l10n_indication'] for i in vaccine['indications'] ]:
     837                          try:
     838                                  no_of_shots4ind, latest_vacc4ind = latest_vaccs[l10n_ind]
     839                                  ago = gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
     840                                  lines.append(_('%s  (most recent shot of %s: %s ago)') % (l10n_ind, no_of_shots4ind, ago))
     841                          except KeyError:
     842                                  lines.append(_('%s  (no previous vaccination recorded)') % l10n_ind)
     843
     844                  self._TCTRL_indications.SetValue(_('Protects against:\n ') + '\n '.join(lines))
     845
     846          #----------------------------------------------------------------
     847          # generic Edit Area mixin API
     848          #----------------------------------------------------------------
     849 -        def _valid_for_save(self):
     850
     851                  has_errors = False
     852
     853                  if not self._PRW_date_given.is_valid_timestamp(empty_is_valid = False):
     854                          has_errors = True
     855
     856                  vaccine = self._PRW_vaccine.GetData(as_instance = True)
     857                  if vaccine is None:
     858                          has_errors = True
     859                          self._PRW_vaccine.display_as_valid(False)
     860                  else:
     861                          self._PRW_vaccine.display_as_valid(True)
     862
     863                  if self._PRW_batch.GetValue().strip() == '':
     864                          has_errors = True
     865                          self._PRW_batch.display_as_valid(False)
     866                  else:
     867                          self._PRW_batch.display_as_valid(True)
     868
     869                  if self._PRW_episode.GetValue().strip() == '':
     870                          self._PRW_episode.SetText(value = _('prevention'))
     871
     872                  return (has_errors is False)
     873
     874          #----------------------------------------------------------------
     875 -        def _save_as_new(self):
     876
     877                  vaccine = self._PRW_vaccine.GetData()
     878                  data = self.__save_new_from_vaccine(vaccine = vaccine)
     879
     880                  # must be done very late or else the property access
     881                  # will refresh the display such that later field
     882                  # access will return empty values
     883                  self.data = data
     884
     885                  return True
     886
     887          #----------------------------------------------------------------
     888 -        def __save_new_from_vaccine(self, vaccine=None):
     889
     890                  emr = gmPerson.gmCurrentPatient().emr
     891
     892                  data = emr.add_vaccination (
     893                          episode = self._PRW_episode.GetData(can_create = True, is_open = False),
     894                          vaccine = vaccine,
     895                          batch_no = self._PRW_batch.GetValue().strip()
     896                  )
     897
     898                  if self._CHBOX_anamnestic.GetValue() is True:
     899                          data['soap_cat'] = 's'
     900                  else:
     901                          data['soap_cat'] = 'p'
     902
     903                  data['date_given'] = self._PRW_date_given.GetData()
     904                  data['site'] = self._PRW_site.GetValue().strip()
     905                  data['pk_provider'] = self._PRW_provider.GetData()
     906                  data['reaction'] = self._PRW_reaction.GetValue().strip()
     907                  data['comment'] = self._TCTRL_comment.GetValue().strip()
     908
     909                  data.save()
     910
     911                  return data
     912
     913          #----------------------------------------------------------------
     914 -        def _save_as_update(self):
     915
     916                  if self._CHBOX_anamnestic.GetValue() is True:
     917                          self.data['soap_cat'] = 's'
     918                  else:
     919                          self.data['soap_cat'] = 'p'
     920
     921                  self.data['date_given'] = self._PRW_date_given.GetData()
     922                  self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
     923                  self.data['batch_no'] = self._PRW_batch.GetValue().strip()
     924                  self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
     925                  self.data['site'] = self._PRW_site.GetValue().strip()
     926                  self.data['pk_provider'] = self._PRW_provider.GetData()
     927                  self.data['reaction'] = self._PRW_reaction.GetValue().strip()
     928                  self.data['comment'] = self._TCTRL_comment.GetValue().strip()
     929
     930                  self.data.save()
     931
     932                  return True
     933
     934          #----------------------------------------------------------------
     935 -        def _refresh_as_new(self):
     936                  self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here())
     937                  self._CHBOX_anamnestic.SetValue(False)
     938                  self._PRW_vaccine.SetText(value = '', data = None, suppress_smarts = True)
     939                  self._PRW_batch.unset_context(context = 'pk_vaccine')
     940                  self._PRW_batch.SetValue('')
     941                  self._PRW_episode.SetText(value = '', data = None, suppress_smarts = True)
     942                  self._PRW_site.SetValue('')
     943                  self._PRW_provider.SetData(data = None)
     944                  self._PRW_reaction.SetText(value = '', data = None, suppress_smarts = True)
     945                  self._BTN_report.Enable(False)
     946                  self._TCTRL_comment.SetValue('')
     947
     948                  self.__refresh_indications()
     949
     950                  self._PRW_date_given.SetFocus()
     951
     952          #----------------------------------------------------------------
     953 -        def _refresh_from_existing(self):
     954                  self._PRW_date_given.SetText(data = self.data['date_given'])
     955                  if self.data['soap_cat'] == 's':
     956                          self._CHBOX_anamnestic.SetValue(True)
     957                  else:
     958                          self._CHBOX_anamnestic.SetValue(False)
     959                  self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
     960
     961                  self._PRW_batch.SetValue(self.data['batch_no'])
     962                  self._PRW_episode.SetData(data = self.data['pk_episode'])
     963                  self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], ''))
     964                  self._PRW_provider.SetData(self.data['pk_provider'])
     965                  self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], ''))
     966                  if self.data['reaction'] is None:
     967                          self._BTN_report.Enable(False)
     968                  else:
     969                          self._BTN_report.Enable(True)
     970                  self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
     971
     972                  self.__refresh_indications()
     973
     974                  self._PRW_date_given.SetFocus()
     975
     976          #----------------------------------------------------------------
     977 -        def _refresh_as_new_from_existing(self):
     978                  self._PRW_date_given.SetText(data = self.data['date_given'])
     979                  #self._CHBOX_anamnestic.SetValue(False)
     980                  self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
     981
     982                  self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine'])
     983                  self._PRW_batch.SetValue('')
     984
     985                  self._PRW_episode.SetData(data = self.data['pk_episode'])
     986                  self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], ''))
     987                  self._PRW_provider.SetData(self.data['pk_provider'])
     988                  self._PRW_reaction.SetValue('')
     989                  self._BTN_report.Enable(False)
     990                  self._TCTRL_comment.SetValue('')
     991
     992                  self.__refresh_indications()
     993
     994                  self._PRW_date_given.SetFocus()
     995
     996          #----------------------------------------------------------------
     997          # event handlers
     998          #----------------------------------------------------------------
     999 -        def _on_report_button_pressed(self, event):
    1000                  event.Skip()
    1001                  dbcfg = gmCfg.cCfgSQL()
    1002                  url = dbcfg.get2 (
    1003                          option = 'external.urls.report_vaccine_ADR',
    1004                          workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1005                          bias = 'user',
    1006                          default = 'http://www.pei.de/cln_042/SharedDocs/Downloads/fachkreise/uaw/meldeboegen/b-ifsg-meldebogen,templateId=raw,property=publicationFile.pdf/b-ifsg-meldebogen.pdf'
    1007                  )
    1008
    1009                  if url.strip() == '':
    1010                          url = dbcfg.get2 (
    1011                                  option = 'external.urls.report_ADR',
    1012                                  workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
    1013                                  bias = 'user'
    1014                          )
    1015                  gmNetworkTools.open_url_in_browser(url = url)
    1016          #----------------------------------------------------------------
    1017 -        def _on_add_vaccine_button_pressed(self, event):
    1018                  edit_vaccine(parent = self, vaccine = None, single_entry = False)
    1019                  # FIXME: could set newly generated vaccine here
    1020
    1021  #======================================================================
    1022  #======================================================================
    1023  #======================================================================
    1024  #======================================================================
    1025 -class cImmunisationsPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
    1026
    1027 -        def __init__(self, parent, id):
    1028                  wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
    1029                  gmRegetMixin.cRegetOnPaintMixin.__init__(self)
    1030                  self.__pat = gmPerson.gmCurrentPatient()
    1031                  # do this here so "import cImmunisationsPanel from gmVaccWidgets" works
    1032                  self.ID_VaccinatedIndicationsList = wx.NewId()
    1033                  self.ID_VaccinationsPerRegimeList = wx.NewId()
    1034                  self.ID_MissingShots = wx.NewId()
    1035                  self.ID_ActiveSchedules = wx.NewId()
    1036                  self.__do_layout()
    1037                  self.__register_interests()
    1038                  self.__reset_ui_content()
    1039          #----------------------------------------------------
    1040 -        def __do_layout(self):
    1041                  #-----------------------------------------------
    1042                  # top part
    1043                  #-----------------------------------------------
    1044                  pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _("  IMMUNISATIONS  "))
    1045                  self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
    1046
    1047                  #-----------------------------------------------
    1048                  # middle part
    1049                  #-----------------------------------------------
    1050                  # divider headings below editing area
    1051                  indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
    1052                  vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
    1053                  schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
    1054                  szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
    1055                  szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
    1056                  szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
    1057                  szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
    1058
    1059                  # left list: indications for which vaccinations have been given
    1060                  self.LBOX_vaccinated_indications = wx.ListBox(
    1061                          parent = self,
    1062                          id = self.ID_VaccinatedIndicationsList,
    1063                          choices = [],
    1064                          style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
    1065                  )
    1066                  self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
    1067
    1068                  # right list: when an indication has been selected on the left
    1069                  # display the corresponding vaccinations on the right
    1070                  self.LBOX_given_shots = wx.ListBox(
    1071                          parent = self,
    1072                          id = self.ID_VaccinationsPerRegimeList,
    1073                          choices = [],
    1074                          style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
    1075                  )
    1076                  self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
    1077
    1078                  self.LBOX_active_schedules = wx.ListBox (
    1079                          parent = self,
    1080                          id = self.ID_ActiveSchedules,
    1081                          choices = [],
    1082                          style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
    1083                  )
    1084                  self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
    1085
    1086                  szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
    1087                  szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
    1088                  szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
    1089                  szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
    1090
    1091                  #---------------------------------------------
    1092                  # bottom part
    1093                  #---------------------------------------------
    1094                  missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
    1095                  szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
    1096                  szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
    1097
    1098                  self.LBOX_missing_shots = wx.ListBox (
    1099                          parent = self,
    1100                          id = self.ID_MissingShots,
    1101                          choices = [],
    1102                          style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
    1103                  )
    1104                  self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
    1105
    1106                  szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
    1107                  szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
    1108
    1109                  # alert caption
    1110                  pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _('  Alerts  '))
    1111
    1112                  #---------------------------------------------
    1113                  # add all elements to the main background sizer
    1114                  #---------------------------------------------
    1115                  self.mainsizer = wx.BoxSizer(wx.VERTICAL)
    1116                  self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
    1117                  self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
    1118                  self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
    1119                  self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
    1120                  self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
    1121                  self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
    1122                  self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
    1123
    1124                  self.SetAutoLayout(True)
    1125                  self.SetSizer(self.mainsizer)
    1126                  self.mainsizer.Fit(self)
    1127          #----------------------------------------------------
    1128 -        def __register_interests(self):
    1129                  # wxPython events
    1130                  wx.EVT_SIZE(self, self.OnSize)
    1131                  wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
    1132                  wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
    1133                  wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
    1134  #               wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton)
    1135
    1136                  # client internal signals
    1137                  gmDispatcher.connect(signal= 'post_patient_selection', receiver=self._schedule_data_reget)
    1138                  gmDispatcher.connect(signal= 'vaccinations_updated', receiver=self._schedule_data_reget)
    1139          #----------------------------------------------------
    1140          # event handlers
    1141          #----------------------------------------------------
    1142 -        def OnSize (self, event):
    1143                  w, h = event.GetSize()
    1144                  self.mainsizer.SetDimension (0, 0, w, h)
    1145          #----------------------------------------------------
    1146 -        def _on_given_shot_selected(self, event):
    1147                  """Paste previously given shot into edit area.
    1148                  """
    1149                  self.editarea.set_data(aVacc=event.GetClientData())
    1150          #----------------------------------------------------
    1151 -        def _on_missing_shot_selected(self, event):
    1152                  self.editarea.set_data(aVacc = event.GetClientData())
    1153          #----------------------------------------------------
    1154 -        def _on_vaccinated_indication_selected(self, event):
    1155                  """Update right hand middle list to show vaccinations given for selected indication."""
    1156                  ind_list = event.GetEventObject()
    1157                  selected_item = ind_list.GetSelection()
    1158                  ind = ind_list.GetClientData(selected_item)
    1159                  # clear list
    1160                  self.LBOX_given_shots.Set([])
    1161                  emr = self.__pat.emr
    1162                  shots = emr.get_vaccinations(indications = [ind])
    1163                  # FIXME: use Set() for entire array (but problem with client_data)
    1164                  for shot in shots:
    1165                          if shot['is_booster']:
    1166                                  marker = 'B'
    1167                          else:
    1168                                  marker = '#%s' % shot['seq_no']
    1169                          label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
    1170                          self.LBOX_given_shots.Append(label, shot)
    1171          #----------------------------------------------------
    1172 -        def __reset_ui_content(self):
    1173                  # clear edit area
    1174                  self.editarea.set_data()
    1175                  # clear lists
    1176                  self.LBOX_vaccinated_indications.Clear()
    1177                  self.LBOX_given_shots.Clear()
    1178                  self.LBOX_active_schedules.Clear()
    1179                  self.LBOX_missing_shots.Clear()
    1180          #----------------------------------------------------
    1181 -        def _populate_with_data(self):
    1182                  # clear lists
    1183                  self.LBOX_vaccinated_indications.Clear()
    1184                  self.LBOX_given_shots.Clear()
    1185                  self.LBOX_active_schedules.Clear()
    1186                  self.LBOX_missing_shots.Clear()
    1187
    1188                  emr = self.__pat.emr
    1189
    1190                  t1 = time.time()
    1191                  # populate vaccinated-indications list
    1192                  # FIXME: consider adding virtual indication "most recent" to
    1193                  # FIXME: display most recent of all indications as suggested by Syan
    1194                  status, indications = emr.get_vaccinated_indications()
    1195                  # FIXME: would be faster to use Set() but can't
    1196                  # use Set(labels, client_data), and have to know
    1197                  # line position in SetClientData :-(
    1198                  for indication in indications:
    1199                          self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
    1200  #               self.LBOX_vaccinated_indications.Set(lines)
    1201  #               self.LBOX_vaccinated_indications.SetClientData(data)
    1202                  print("vaccinated indications took", time.time()-t1, "seconds")
    1203
    1204                  t1 = time.time()
    1205                  # populate active schedules list
    1206                  scheds = emr.get_scheduled_vaccination_regimes()
    1207                  if scheds is None:
    1208                          label = _('ERROR: cannot retrieve active vaccination schedules')
    1209                          self.LBOX_active_schedules.Append(label)
    1210                  elif len(scheds) == 0:
    1211                          label = _('no active vaccination schedules')
    1212                          self.LBOX_active_schedules.Append(label)
    1213                  else:
    1214                          for sched in scheds:
    1215                                  label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
    1216                                  self.LBOX_active_schedules.Append(label)
    1217                  print("active schedules took", time.time()-t1, "seconds")
    1218
    1219                  t1 = time.time()
    1220                  # populate missing-shots list
    1221                  missing_shots = emr.get_missing_vaccinations()
    1222                  print("getting missing shots took", time.time()-t1, "seconds")
    1223                  if missing_shots is None:
    1224                          label = _('ERROR: cannot retrieve due/overdue vaccinations')
    1225                          self.LBOX_missing_shots.Append(label, None)
    1226                          return True
    1227                  # due
    1228                  due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
    1229                  overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
    1230                  for shot in missing_shots['due']:
    1231                          if shot['overdue']:
    1232                                  years, days_left = divmod(shot['amount_overdue'].days, 364.25)
    1233                                  weeks = days_left / 7
    1234                                  # amount_overdue, seq_no, indication, regime, vacc_comment
    1235                                  label = overdue_template % (
    1236                                          years,
    1237                                          weeks,
    1238                                          shot['seq_no'],
    1239                                          shot['l10n_indication'],
    1240                                          shot['regime'],
    1241                                          shot['vacc_comment']
    1242                                  )
    1243                                  self.LBOX_missing_shots.Append(label, shot)
    1244                          else:
    1245                                  # time_left, seq_no, regime, latest_due, vacc_comment
    1246                                  label = due_template % (
    1247                                          shot['time_left'].days / 7,
    1248                                          shot['seq_no'],
    1249                                          shot['indication'],
    1250                                          shot['regime'],
    1251                                          shot['latest_due'].strftime('%m/%Y'),
    1252                                          shot['vacc_comment']
    1253                                  )
    1254                                  self.LBOX_missing_shots.Append(label, shot)
    1255                  # booster
    1256                  lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
    1257                  for shot in missing_shots['boosters']:
    1258                          # indication, regime, vacc_comment
    1259                          label = lbl_template % (
    1260                                  shot['l10n_indication'],
    1261                                  shot['regime'],
    1262                                  shot['vacc_comment']
    1263                          )
    1264                          self.LBOX_missing_shots.Append(label, shot)
    1265                  print("displaying missing shots took", time.time()-t1, "seconds")
    1266
    1267                  return True
    1268          #----------------------------------------------------
    1269 -        def _on_post_patient_selection(self, **kwargs):
    1270                  return 1
    1271                  # FIXME:
    1272  #               if has_focus:
    1273  #                       wxCallAfter(self.__reset_ui_content)
    1274  #               else:
    1275  #                       return 1
    1276          #----------------------------------------------------
    1277 -        def _on_vaccinations_updated(self, **kwargs):
    1278                  return 1
    1279                  # FIXME:
    1280  #               if has_focus:
    1281  #                       wxCallAfter(self.__reset_ui_content)
    1282  #               else:
    1283  #                       is_stale == True
    1284  #                       return 1
    1285  #======================================================================
    1286  # main
    1287  #----------------------------------------------------------------------
    1288  if __name__ == "__main__":
    1289
    1290          if len(sys.argv) < 2:
    1291                  sys.exit()
    1292
    1293          if sys.argv[1] != 'test':
    1294                  sys.exit()
    1295
    1296          app = wx.PyWidgetTester(size = (600, 600))
    1297          app.SetWidget(cXxxPhraseWheel, -1)
    1298          app.MainLoop()
    1299