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

# Source Code for Module Gnumed.wxpython.gmAddressWidgets

       1  """GNUmed generic address related widgets."""
       2  #================================================================
       3  __author__ = 'karsten.hilbert@gmx.net'
       4  __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
       5
       6  # stdlib
       7  import logging, sys
       8
       9
      10  # 3rd party
      11  import wx
      12
      13
      14  # GNUmed
      15  if __name__ == '__main__':
      16          sys.path.insert(0, '../../')
      17
      18  from Gnumed.pycommon import gmTools
      19  from Gnumed.pycommon import gmMatchProvider
      20  from Gnumed.business import gmDemographicRecord
      21
      22  from Gnumed.wxpython import gmCfgWidgets
      23  from Gnumed.wxpython import gmPhraseWheel
      24  from Gnumed.wxpython import gmListWidgets
      25  from Gnumed.wxpython import gmEditArea
      26  from Gnumed.wxpython import gmGuiHelpers
      27
      28
      29  _log = logging.getLogger('gm.ui')
      30  #============================================================
      31  # country related widgets / functions
      32  #============================================================
      33 -def configure_default_country(parent=None):
      34
      35          if parent is None:
      36                  parent = wx.GetApp().GetTopWindow()
      37
      38          countries = gmDemographicRecord.get_countries()
      39
      40          gmCfgWidgets.configure_string_from_list_option (
      41                  parent = parent,
      42                  message = _('Select the default country for new persons.\n'),
      43                  option = 'person.create.default_country',
      44                  bias = 'user',
      45                  choices = [ (c['l10n_country'], c['code']) for c in countries ],
      46                  columns = [_('Country'), _('Code')],
      47                  data = [ c['code'] for c in countries ]
      48          )
      49  #============================================================
      50 -class cCountryPhraseWheel(gmPhraseWheel.cPhraseWheel):
      51
      52 -        def __init__(self, *args, **kwargs):
      53
      54                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
      55
      56                  context = {
      57                          u'ctxt_zip': {
      58                                  u'where_part': u'and zip ilike %(zip)s',
      59                                  u'placeholder': u'zip'
      60                          }
      61                  }
      62                  query = u"""
      63  SELECT
      64          data,
      65          field_label,
      66          list_label
      67  FROM (
      68          SELECT DISTINCT ON (data)
      69                  data,
      70                  field_label,
      71                  list_label,
      72                  rank
      73          FROM (
      74
      75          -- localized to user
      76                          SELECT
      77                                  code_country AS data,
      78                                  l10n_country AS field_label,
      79                                  l10n_country || ' (' || code_country || '): ' || country AS list_label,
      80                                  1 AS rank
      81                          FROM dem.v_zip2data
      82                          WHERE
      83                                  l10n_country %(fragment_condition)s
      84                                  %(ctxt_zip)s
      85                  UNION ALL
      86                          SELECT
      87                                  code AS data,
      88                                  _(name) AS field_label,
      89                                  _(name) || ' (' || code || '): ' || name AS list_label,
      90                                  2 AS rank
      91                          FROM dem.country
      92                          WHERE
      93                                  _(name) %(fragment_condition)s
      94
      95                  UNION ALL
      96
      97          -- non-localized
      98                          SELECT
      99                                  code_country AS data,
     100                                  l10n_country AS field_label,
     101                                  country || ' (' || code_country || '): ' || l10n_country AS list_label,
     102                                  3 AS rank
     103                          FROM dem.v_zip2data
     104                          WHERE
     105                                  country %(fragment_condition)s
     106                                  %(ctxt_zip)s
     107                  UNION ALL
     108                          SELECT
     109                                  code AS data,
     110                                  _(name) AS field_label,
     111                                  name || ' (' || code || '): ' || _(name) AS list_label,
     112                                  4 AS rank
     113                          FROM dem.country
     114                          WHERE
     115                                  name %(fragment_condition)s
     116
     117                  UNION ALL
     118
     119          -- abbreviation
     120                          SELECT
     121                                  code AS data,
     122                                  _(name) AS field_label,
     123                                  code || ': ' || _(name) || ' (' || name || ')' AS list_label,
     124                                  5 AS rank
     125                          FROM dem.country
     126                          WHERE
     127                                  code %(fragment_condition)s
     128
     129          ) AS candidates
     130  ) AS distint_candidates
     131  ORDER BY rank, list_label
     132  LIMIT 25"""
     133                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
     134                  mp._SQL_data2match = u"""
     135                          SELECT
     136                                  code AS data,
     137                                  _(name) AS field_label,
     138                                  code || ': ' || _(name) || ' (' || name || ')' AS list_label,
     139                                  5 AS rank
     140                          FROM dem.country
     141                          WHERE
     142                                  code = %(pk)s
     143                  """
     144                  mp.setThresholds(2, 5, 9)
     145                  self.matcher = mp
     146
     147                  self.unset_context(context = u'zip')
     148                  self.SetToolTipString(_('Type or select a country.'))
     149                  self.capitalisation_mode = gmTools.CAPS_FIRST
     150                  self.selection_only = True
     151
     152  #============================================================
     153  # province/state related widgets / functions
     154  #============================================================
     155 -def configure_default_region(parent=None):
     156
     157          if parent is None:
     158                  parent = wx.GetApp().GetTopWindow()
     159
     160          provs = gmDemographicRecord.get_provinces()
     161
     162          gmCfgWidgets.configure_string_from_list_option (
     163                  parent = parent,
     164                  message = _('Select the default region/province/state/territory for new persons.\n'),
     165                  option = 'person.create.default_region',
     166                  bias = 'user',
     167                  choices = [ (p['l10n_country'], p['l10n_state'], p['code_state']) for p in provs ],
     168                  columns = [_('Country'), _('Region'), _('Code')],
     169                  data = [ p['state'] for p in provs ]
     170          )
     171  #============================================================
     172 -def edit_province(parent=None, province=None):
     173          ea = cProvinceEAPnl(parent = parent, id = -1, province = province)
     174          dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (province is not None))
     175          dlg.SetTitle(gmTools.coalesce(province, _('Adding province'), _('Editing province')))
     176          result = dlg.ShowModal()
     177          dlg.Destroy()
     178          return (result == wx.ID_OK)
     179  #============================================================
     180 -def delete_province(parent=None, province=None):
     181
     182          msg = _(
     183                  'Are you sure you want to delete this province ?\n'
     184                  '\n'
     185                  'Deletion will only work if this province is not\n'
     186                  'yet in use in any patient addresses.'
     187          )
     188
     189          tt = _(
     190                  'Also delete any towns/cities/villages known\n'
     191                  'to be situated in this state as long as\n'
     192                  'no patients are recorded to live there.'
     193          )
     194
     195          dlg = gmGuiHelpers.c2ButtonQuestionDlg (
     196                  parent,
     197                  -1,
     198                  caption = _('Deleting province'),
     199                  question = msg,
     200                  show_checkbox = True,
     201                  checkbox_msg = _('delete related townships'),
     202                  checkbox_tooltip = tt,
     203                  button_defs = [
     204                          {'label': _('Yes, delete'), 'tooltip': _('Delete province and possibly related townships.'), 'default': False},
     205                          {'label': _('No'), 'tooltip': _('No, do NOT delete anything.'), 'default': True}
     206                  ]
     207          )
     208
     209          decision = dlg.ShowModal()
     210          if decision != wx.ID_YES:
     211                  dlg.Destroy()
     212                  return False
     213
     214          include_urbs = dlg.checkbox_is_checked()
     215          dlg.Destroy()
     216
     217          return gmDemographicRecord.delete_province(province = province, delete_urbs = include_urbs)
     218  #============================================================
     219 -def manage_provinces(parent=None):
     220
     221          if parent is None:
     222                  parent = wx.GetApp().GetTopWindow()
     223
     224          #------------------------------------------------------------
     225          def delete(province=None):
     226                  return delete_province(parent = parent, province = province['pk_state'])
     227          #------------------------------------------------------------
     228          def edit(province=None):
     229                  return edit_province(parent = parent, province = province)
     230          #------------------------------------------------------------
     231          def refresh(lctrl):
     232                  wx.BeginBusyCursor()
     233                  provinces = gmDemographicRecord.get_provinces()
     234                  lctrl.set_string_items([ (p['l10n_country'], p['l10n_state']) for p in provinces ])
     235                  lctrl.set_data(provinces)
     236                  wx.EndBusyCursor()
     237          #------------------------------------------------------------
     238          msg = _(
     239                  '\n'
     240                  'This list shows the provinces known to GNUmed.\n'
     241                  '\n'
     242                  'In your jurisdiction "province" may correspond to either of "state",\n'
     243                  '"county", "region", "territory", or some such term.\n'
     244                  '\n'
     245                  'Select the province you want to edit !\n'
     246          )
     247
     248          gmListWidgets.get_choices_from_list (
     249                  parent = parent,
     250                  msg = msg,
     251                  caption = _('Editing provinces ...'),
     252                  columns = [_('Country'), _('Province')],
     253                  single_selection = True,
     254                  new_callback = edit,
     255                  #edit_callback = edit,
     256                  delete_callback = delete,
     257                  refresh_callback = refresh
     258          )
     259  #============================================================
     260 -class cStateSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
     261
     262 -        def __init__(self, *args, **kwargs):
     263
     264                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     265
     266                  context = {
     267                          u'ctxt_country_name': {
     268                                  u'where_part': u'AND l10n_country ILIKE %(country_name)s OR country ILIKE %(country_name)s',
     269                                  u'placeholder': u'country_name'
     270                          },
     271                          u'ctxt_zip': {
     272                                  u'where_part': u'AND zip ilike %(zip)s',
     273                                  u'placeholder': u'zip'
     274                          },
     275                          u'ctxt_country_code': {
     276                                  u'where_part': u'AND country IN (SELECT code FROM dem.country WHERE _(name) ILIKE %(country_name)s OR name ILIKE %(country_name)s)',
     277                                  u'placeholder': u'country_name'
     278                          }
     279                  }
     280
     281                  query = u"""
     282  SELECT
     283          data,
     284          field_label,
     285          list_label
     286  FROM (
     287          SELECT DISTINCT ON (field_label)
     288                  data,
     289                  field_label,
     290                  list_label,
     291                  rank
     292          FROM (
     293                          -- 1: find states based on name, context: zip and country name
     294                          SELECT
     295                                  code_state AS data,
     296                                  state AS field_label,
     297                                  state || ' (' || code_state || '), ' || l10n_country || ' (' || code_country || ')' AS list_label,
     298                                  1 AS rank
     299                          FROM dem.v_zip2data
     300                          WHERE
     301                                  state %(fragment_condition)s
     302                                  %(ctxt_country_name)s
     303                                  %(ctxt_zip)s
     304
     305                  UNION ALL
     306
     307                          -- 2: find states based on code, context: zip and country name
     308                          SELECT
     309                                  code_state AS data,
     310                                  state AS field_label,
     311                                  code_state || ': ' || state || ' (' || l10n_country || ', ' || code_country || ')' AS list_label,
     312                                  2 AS rank
     313                          FROM dem.v_zip2data
     314                          WHERE
     315                                  code_state %(fragment_condition)s
     316                                  %(ctxt_country_name)s
     317                                  %(ctxt_zip)s
     318
     319                  UNION ALL
     320
     321                          -- 3: find states based on name, context: country
     322                          SELECT
     323                                  code AS data,
     324                                  name AS field_label,
     325                                  name || ' (' || code || '), ' || country AS list_label,
     326                                  3 AS rank
     327                          FROM dem.state
     328                          WHERE
     329                                  name %(fragment_condition)s
     330                                  %(ctxt_country_code)s
     331
     332                  UNION ALL
     333
     334                          -- 4: find states based on code, context: country
     335                          SELECT
     336                                  code AS data,
     337                                  name AS field_label,
     338                                  code || ': ' || name || ', ' || country AS list_label,
     339                                  3 AS rank
     340                          FROM dem.state
     341                          WHERE
     342                                  code %(fragment_condition)s
     343                                  %(ctxt_country_code)s
     344
     345          ) AS candidate_states
     346  ) AS distinct_matches
     347  ORDER BY rank, list_label
     348  LIMIT 50"""
     349
     350                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
     351                  mp.setThresholds(2, 5, 6)
     352                  mp.word_separators = u'[ \t]+'
     353                  self.matcher = mp
     354
     355                  self.unset_context(context = u'zip')
     356                  self.unset_context(context = u'country_name')
     357                  self.SetToolTipString(_('Type or select a state/region/province/territory.'))
     358                  self.capitalisation_mode = gmTools.CAPS_FIRST
     359                  self.selection_only = True
     360  #====================================================================
     361  from Gnumed.wxGladeWidgets import wxgProvinceEAPnl
     362
     363 -class cProvinceEAPnl(wxgProvinceEAPnl.wxgProvinceEAPnl, gmEditArea.cGenericEditAreaMixin):
     364
     365 -        def __init__(self, *args, **kwargs):
     366
     367                  try:
     368                          data = kwargs['province']
     369                          del kwargs['province']
     370                  except KeyError:
     371                          data = None
     372
     373                  wxgProvinceEAPnl.wxgProvinceEAPnl.__init__(self, *args, **kwargs)
     374                  gmEditArea.cGenericEditAreaMixin.__init__(self)
     375
     376                  self.mode = 'new'
     377                  self.data = data
     378                  if data is not None:
     379                          self.mode = 'edit'
     380
     381                  self.__init_ui()
     382          #----------------------------------------------------------------
     383 -        def __init_ui(self):
     384                  self._PRW_province.selection_only = False
     385          #----------------------------------------------------------------
     386          # generic Edit Area mixin API
     387          #----------------------------------------------------------------
     388 -        def _valid_for_save(self):
     389
     390                  validity = True
     391
     392                  if self._PRW_province.GetData() is None:
     393                          if self._PRW_province.GetValue().strip() == u'':
     394                                  validity = False
     395                                  self._PRW_province.display_as_valid(False)
     396                          else:
     397                                  self._PRW_province.display_as_valid(True)
     398                  else:
     399                          self._PRW_province.display_as_valid(True)
     400
     401                  if self._PRW_province.GetData() is None:
     402                          if self._TCTRL_code.GetValue().strip() == u'':
     403                                  validity = False
     404                                  self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_invalid)
     405                          else:
     406                                  self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_valid)
     407
     408                  if self._PRW_country.GetData() is None:
     409                          validity = False
     410                          self._PRW_country.display_as_valid(False)
     411                  else:
     412                          self._PRW_country.display_as_valid(True)
     413
     414                  return validity
     415          #----------------------------------------------------------------
     416 -        def _save_as_new(self):
     417                  gmDemographicRecord.create_province (
     418                          name = self._PRW_province.GetValue().strip(),
     419                          code = self._TCTRL_code.GetValue().strip(),
     420                          country = self._PRW_country.GetData()
     421                  )
     422
     423                  # EA is refreshed automatically after save, so need this ...
     424                  self.data = {
     425                          'l10n_state' : self._PRW_province.GetValue().strip(),
     426                          'code_state' : self._TCTRL_code.GetValue().strip(),
     427                          'l10n_country' : self._PRW_country.GetValue().strip()
     428                  }
     429
     430                  return True
     431          #----------------------------------------------------------------
     432 -        def _save_as_update(self):
     433                  # update self.data and save the changes
     434                  #self.data[''] =
     435                  #self.data[''] =
     436                  #self.data[''] =
     437                  #self.data.save()
     438
     439                  # do nothing for now (IOW, don't support updates)
     440                  return True
     441          #----------------------------------------------------------------
     442 -        def _refresh_as_new(self):
     443                  self._PRW_province.SetText()
     444                  self._TCTRL_code.SetValue(u'')
     445                  self._PRW_country.SetText()
     446
     447                  self._PRW_province.SetFocus()
     448          #----------------------------------------------------------------
     449 -        def _refresh_from_existing(self):
     450                  self._PRW_province.SetText(self.data['l10n_state'], self.data['code_state'])
     451                  self._TCTRL_code.SetValue(self.data['code_state'])
     452                  self._PRW_country.SetText(self.data['l10n_country'], self.data['code_country'])
     453
     454                  self._PRW_province.SetFocus()
     455          #----------------------------------------------------------------
     456 -        def _refresh_as_new_from_existing(self):
     457                  self._PRW_province.SetText()
     458                  self._TCTRL_code.SetValue(u'')
     459                  self._PRW_country.SetText(self.data['l10n_country'], self.data['code_country'])
     460
     461                  self._PRW_province.SetFocus()
     462
     463  #============================================================
     464  # other address parts phrasewheels and widgets
     465  #============================================================
     466 -class cZipcodePhraseWheel(gmPhraseWheel.cPhraseWheel):
     467
     468 -        def __init__(self, *args, **kwargs):
     469                  # FIXME: add possible context
     470                  query = u"""
     471                          (SELECT distinct postcode, postcode FROM dem.street WHERE postcode %(fragment_condition)s limit 20)
     472                                  UNION
     473                          (SELECT distinct postcode, postcode FROM dem.urb WHERE postcode %(fragment_condition)s limit 20)"""
     474                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     475                  mp.setThresholds(2, 3, 15)
     476                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     477                  self.SetToolTipString(_("Type or select a zip code (postcode).\n\nUse e.g. '?' if unknown."))
     478                  self.matcher = mp
     479  #============================================================
     480 -class cStreetPhraseWheel(gmPhraseWheel.cPhraseWheel):
     481
     482 -        def __init__(self, *args, **kwargs):
     483                  context = {
     484                          u'ctxt_zip': {
     485                                  u'where_part': u'AND zip ILIKE %(zip)s',
     486                                  u'placeholder': u'zip'
     487                          }
     488                  }
     489                  query = u"""
     490          SELECT
     491                  data,
     492                  field_label,
     493                  list_label
     494          FROM (
     495
     496                          SELECT DISTINCT ON (data)
     497                                  street AS data,
     498                                  street AS field_label,
     499                                  street || ' (' || zip || ', ' || urb || coalesce(', ' || suburb, '') || ', ' || l10n_country || ')' AS list_label,
     500                                  1 AS rank
     501                          FROM dem.v_zip2data
     502                          WHERE
     503                                  street %(fragment_condition)s
     504                                  %(ctxt_zip)s
     505
     506                  UNION ALL
     507
     508                          SELECT DISTINCT ON (data)
     509                                  name AS data,
     510                                  name AS field_label,
     511                                  name || ' (' || postcode || coalesce(', ' || suburb, '') || ')' AS list_label,
     512                                  2 AS rank
     513                          FROM dem.street
     514                          WHERE
     515                                  name %(fragment_condition)s
     516
     517          ) AS matching_streets
     518          ORDER BY rank, field_label
     519          LIMIT 50"""
     520                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
     521                  mp.setThresholds(3, 5, 8)
     522                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
     523                  self.unset_context(context = u'zip')
     524
     525                  self.SetToolTipString(_('Type or select a street.'))
     526                  self.capitalisation_mode = gmTools.CAPS_FIRST
     527                  self.matcher = mp
     528  #============================================================
     529 -class cSuburbPhraseWheel(gmPhraseWheel.cPhraseWheel):
     530
     531 -        def __init__(self, *args, **kwargs):
     532
     533                  query = """
     534  SELECT DISTINCT ON (suburb) suburb, suburb
     535  FROM dem.street
     536  WHERE suburb %(fragment_condition)s
     537  ORDER BY suburb
     538  LIMIT 50
     539  """
     540                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     541                  mp.setThresholds(2, 3, 6)
     542                  gmPhraseWheel.cPhraseWheel.__init__ (
     543                          self,
     544                          *args,
     545                          **kwargs
     546                  )
     547
     548                  self.SetToolTipString(_('Type or select the suburb.'))
     549                  self.capitalisation_mode = gmTools.CAPS_FIRST
     550                  self.matcher = mp
     551  #============================================================
     552 -class cUrbPhraseWheel(gmPhraseWheel.cPhraseWheel):
     553
     554 -        def __init__(self, *args, **kwargs):
     555                  context = {
     556                          u'ctxt_zip': {
     557                                  u'where_part': u'and zip ilike %(zip)s',
     558                                  u'placeholder': u'zip'
     559                          }
     560                  }
     561                  query = u"""
     562          SELECT DISTINCT ON (rank, data)
     563                  data,
     564                  field_label,
     565                  list_label
     566          FROM (
     567
     568                          SELECT
     569                                  urb AS data,
     570                                  urb AS field_label,
     571                                  urb || ' (' || zip || ', ' || state || ', ' || l10n_country || ')' AS list_label,
     572                                  1 AS rank
     573                          FROM dem.v_zip2data
     574                          WHERE
     575                                  urb %(fragment_condition)s
     576                                  %(ctxt_zip)s
     577
     578                  UNION ALL
     579
     580                          SELECT
     581                                  name AS data,
     582                                  name AS field_label,
     583                                  name || ' (' || postcode ||')' AS list_label,
     584                                  2 AS rank
     585                          FROM dem.urb
     586                          WHERE
     587                                  name %(fragment_condition)s
     588
     589          ) AS matching_urbs
     590          ORDER BY rank, data
     591          LIMIT 50"""
     592                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context)
     593                  mp.setThresholds(3, 5, 7)
     594                  gmPhraseWheel.cPhraseWheel.__init__ (
     595                          self,
     596                          *args,
     597                          **kwargs
     598                  )
     599                  self.unset_context(context = u'zip')
     600
     601                  self.SetToolTipString(_('Type or select a city/town/village/dwelling.'))
     602                  self.capitalisation_mode = gmTools.CAPS_FIRST
     603                  self.matcher = mp
     604
     605  #============================================================
     606  # address phrasewheels and widgets
     607  #============================================================
     608 -def manage_addresses(parent=None):
     609
     610          if parent is None:
     611                  parent = wx.GetApp().GetTopWindow()
     612
     613          #------------------------------------------------------------
     614          def calculate_tooltip(address):
     615                  return u'\n'.join(address.format())
     616          #------------------------------------------------------------
     617          def delete(address):
     618                  return gmDemographicRecord.delete_address(pk_address = address['pk_address'])
     619          #------------------------------------------------------------
     620          def refresh(lctrl):
     621                  adrs = gmDemographicRecord.get_addresses(order_by = u'l10n_country, urb, street, number, subunit')
     622                  items = [ [
     623                                  a['street'],
     624                                  gmTools.coalesce(a['notes_street'], u''),
     625                                  a['number'],
     626                                  gmTools.coalesce(a['subunit'], u''),
     627                                  a['postcode'],
     628                                  a['urb'],
     629                                  gmTools.coalesce(a['suburb'], u''),
     630                                  a['l10n_state'],
     631                                  a['l10n_country'],
     632                                  gmTools.coalesce(a['notes_subunit'], u'')
     633                          ] for a in adrs
     634                  ]
     635                  lctrl.set_string_items(items)
     636                  lctrl.set_data(adrs)
     637
     638          #------------------------------------------------------------
     639          cols = [
     640                  _('Street'),
     641                  _('Street info'),
     642                  _('Number'),
     643                  _('Subunit'),
     644                  _('Postal code'),
     645                  _('Community'),
     646                  _('Suburb'),
     647                  _('Region'),
     648                  _('Country'),
     649                  _('Comment')
     650          ]
     651          return gmListWidgets.get_choices_from_list (
     652                  parent = parent,
     653                  caption = _('Showing addresses registered in GNUmed.'),
     654                  columns = cols,
     655                  single_selection = True,
     656                  refresh_callback = refresh,
     657                  delete_callback = delete,
     658                  list_tooltip_callback = calculate_tooltip
     659          )
     660  #============================================================
     661  from Gnumed.wxGladeWidgets import wxgGenericAddressEditAreaPnl
     662
     663 -class cAddressEditAreaPnl(wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl):
     664          """An edit area for editing/creating an address."""
     665
     666 -        def __init__(self, *args, **kwargs):
     667                  try:
     668                          self.__address = kwargs['address']
     669                          del kwargs['address']
     670                  except KeyError:
     671                          self.__address = None
     672
     673                  wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl.__init__(self, *args, **kwargs)
     674
     675                  self.address_holder = None
     676                  self.type_is_editable = True
     677                  self.address_is_searchable = False
     678
     679                  self.__register_interests()
     680                  self.refresh()
     681          #--------------------------------------------------------
     682          # external API
     683          #--------------------------------------------------------
     684 -        def refresh(self, address = None):
     685                  if address is not None:
     686                          self.__address = address
     687
     688                  if self.__address is None:
     689                          self._PRW_type.SetText(u'', None)
     690                          self._PRW_zip.SetText(u'', None)
     691                          self._PRW_street.SetText(u'', None)
     692                          self._TCTRL_notes_street.SetValue(u'')
     693                          self._TCTRL_number.SetValue(u'')
     694                          self._TCTRL_subunit.SetValue(u'')
     695                          self._PRW_suburb.SetText(u'', None)
     696                          self._PRW_urb.SetText(u'', None)
     697                          self._PRW_state.SetText(u'', None)
     698                          self._PRW_country.SetText(u'', None)
     699                          self._TCTRL_notes_subunit.SetValue(u'')
     700                          if self.__type_is_editable:
     701                                  self._PRW_type.SetFocus()
     702                          else:
     703                                  self._PRW_zip.SetFocus()
     704                          return
     705
     706                  if self.__type_is_editable:
     707                          self._PRW_type.SetText(self.address['l10n_address_type'])
     708                  else:
     709                          self._PRW_type.SetText(u'', None)
     710                  self._PRW_zip.SetText(self.address['postcode'])
     711                  self._PRW_street.SetText(self.address['street'], data = self.address['street'])
     712                  self._TCTRL_notes_street.SetValue(gmTools.coalesce(self.address['notes_street'], ''))
     713                  self._TCTRL_number.SetValue(self.address['number'])
     714                  self._TCTRL_subunit.SetValue(gmTools.coalesce(self.address['subunit'], ''))
     715                  self._PRW_suburb.SetText(gmTools.coalesce(self.address['suburb'], ''))
     716                  self._PRW_urb.SetText(self.address['urb'], data = self.address['urb'])
     717                  self._PRW_state.SetText(self.address['l10n_state'], data = self.address['code_state'])
     718                  self._PRW_country.SetText(self.address['l10n_country'], data = self.address['code_country'])
     719                  self._TCTRL_notes_subunit.SetValue(gmTools.coalesce(self.address['notes_subunit'], ''))
     720
     721                  if self.__type_is_editable:
     722                          self._PRW_type.SetFocus()
     723                  else:
     724                          self._PRW_zip.SetFocus()
     725                  return
     726          #--------------------------------------------------------
     727 -        def save(self):
     728                  """Links address to patient or org, creating new address if necessary"""
     729
     730                  if not self.__valid_for_save():
     731                          return False
     732
     733                  try:
     734                          address = gmDemographicRecord.create_address (
     735                                  country = self._PRW_country.GetData(),
     736                                  state = self._PRW_state.GetData(),
     737                                  urb = self._PRW_urb.GetValue().strip(),
     738                                  suburb = gmTools.none_if(self._PRW_suburb.GetValue().strip(), u''),
     739                                  postcode = self._PRW_zip.GetValue().strip(),
     740                                  street = self._PRW_street.GetValue().strip(),
     741                                  number = self._TCTRL_number.GetValue().strip(),
     742                                  subunit = gmTools.none_if(self._TCTRL_subunit.GetValue().strip(), u'')
     743                          )
     744                  except:
     745                          _log.exception('cannot save address')
     746                          gmGuiHelpers.gm_show_error (
     747                                  _('Cannot save address.\n\n'
     748                                    'Does the state [%s]\n'
     749                                    'exist in country [%s] ?'
     750                                  ) % (
     751                                          self._PRW_state.GetValue().strip(),
     752                                          self._PRW_country.GetValue().strip()
     753                                  ),
     754                                  _('Saving address')
     755                          )
     756                          return False
     757
     758                  # link address to owner
     759                  a = self.address_holder.link_address(id_type = self._PRW_type.GetData(), address = address)
     760                  if a['pk_address'] != address['pk_address']:
     761                          raise ValueError('problem linking address to person or org')
     762
     763                  notes = self._TCTRL_notes_street.GetValue().strip()
     764                  if notes != u'':
     765                          address['notes_street'] = notes
     766                  notes = self._TCTRL_notes_subunit.GetValue().strip()
     767                  if notes != u'':
     768                          address['notes_subunit'] = notes
     769                  address.save_payload()
     770
     771                  self.__address = address
     772
     773                  return True
     774          #--------------------------------------------------------
     775          # event handling
     776          #--------------------------------------------------------
     777 -        def __register_interests(self):
     778                  self._PRW_zip.add_callback_on_lose_focus(self._on_zip_set)
     779                  self._PRW_country.add_callback_on_lose_focus(self._on_country_set)
     780          #--------------------------------------------------------
     781 -        def _on_zip_set(self):
     782                  """Set the street, town, state and country according to entered zip code."""
     783                  zip_code = self._PRW_zip.GetValue()
     784                  if zip_code.strip() == u'':
     785                          self._PRW_street.unset_context(context = u'zip')
     786                          self._PRW_urb.unset_context(context = u'zip')
     787                          self._PRW_state.unset_context(context = u'zip')
     788                          self._PRW_country.unset_context(context = u'zip')
     789                  else:
     790                          self._PRW_street.set_context(context = u'zip', val = zip_code)
     791                          self._PRW_urb.set_context(context = u'zip', val = zip_code)
     792                          self._PRW_state.set_context(context = u'zip', val = zip_code)
     793                          self._PRW_country.set_context(context = u'zip', val = zip_code)
     794          #--------------------------------------------------------
     795 -        def _on_country_set(self):
     796                  """Set the states according to entered country."""
     797                  country = self._PRW_country.GetData()
     798                  if country is None:
     799                          self._PRW_state.unset_context(context = 'country')
     800                  else:
     801                          self._PRW_state.set_context(context = 'country', val = country)
     802          #--------------------------------------------------------
     803          # internal helpers
     804          #--------------------------------------------------------
     805 -        def __valid_for_save(self):
     806
     807                  # validate required fields
     808                  is_any_field_filled = False
     809
     810                  required_fields = [
     811                          self._PRW_zip,
     812                          self._PRW_street,
     813                          self._TCTRL_number,
     814                          self._PRW_urb
     815                  ]
     816                  if self.__type_is_editable:
     817                          required_fields.insert(0, self._PRW_type)
     818
     819                  for field in required_fields:
     820                          if len(field.GetValue().strip()) == 0:
     821                                  if is_any_field_filled:
     822                                          field.SetBackgroundColour('pink')
     823                                          field.SetFocus()
     824                                          field.Refresh()
     825                                          gmGuiHelpers.gm_show_error (
     826                                                  _('Address details must be filled in completely or not at all.'),
     827                                                  _('Saving contact data')
     828                                          )
     829                                          return False
     830                          else:
     831                                  is_any_field_filled = True
     832                                  field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
     833                                  field.Refresh()
     834
     835                  required_fields = (
     836                          self._PRW_state,
     837                          self._PRW_country
     838                  )
     839                  for field in required_fields:
     840                          if field.GetData() is None:
     841                                  if is_any_field_filled:
     842                                          field.SetBackgroundColour('pink')
     843                                          field.SetFocus()
     844                                          field.Refresh()
     845                                          gmGuiHelpers.gm_show_error (
     846                                                  _('Address details must be filled in completely or not at all.'),
     847                                                  _('Saving contact data')
     848                                          )
     849                                          return False
     850                          else:
     851                                  is_any_field_filled = True
     852                                  field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
     853                                  field.Refresh()
     854
     855                  return True
     856          #--------------------------------------------------------
     857          # properties
     858          #--------------------------------------------------------
     859 -        def _get_type_is_editable(self):
     860                  return self.__type_is_editable
     861
     862 -        def _set_type_is_editable(self, type_is_editable):
     863                  self.__type_is_editable = type_is_editable
     864                  self._PRW_type.Enable(type_is_editable)
     865                  self._PRW_type.Show(type_is_editable)
     866                  self._LBL_type.Show(type_is_editable)
     867
     868          type_is_editable = property(_get_type_is_editable, _set_type_is_editable)
     869          #--------------------------------------------------------
     870 -        def _get_address_is_searchable(self):
     871                  return self.__address_is_searchable
     872
     873 -        def _set_address_is_searchable(self, address_is_searchable):
     874                  # always set tot FALSE when self.mode == 'new'
     875                  self.__address_is_searchable = address_is_searchable
     876                  self._PRW_address_searcher.Enable(address_is_searchable)
     877                  self._PRW_address_searcher.Show(address_is_searchable)
     878                  self._LBL_search.Show(address_is_searchable)
     879
     880          address_is_searchable = property(_get_address_is_searchable, _set_address_is_searchable)
     881          #--------------------------------------------------------
     882 -        def _get_address(self):
     883                  return self.__address
     884
     885 -        def _set_address(self, address):
     886                  self.__address = address
     887                  self.refresh()
     888
     889          address = property(_get_address, _set_address)
     890
     891  #============================================================
     892 -class cAddressTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
     893
     894 -        def __init__(self, *args, **kwargs):
     895
     896                  query = u"""
     897  SELECT id, type FROM ((
     898          SELECT id, _(name) AS type, 1 AS rank
     899          FROM dem.address_type
     900          WHERE _(name) %(fragment_condition)s
     901  ) UNION (
     902          SELECT id, name AS type, 2 AS rank
     903          FROM dem.address_type
     904          WHERE name %(fragment_condition)s
     905  )) AS ur
     906  order by
     907          ur.rank, ur.type
     908  """
     909                  mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
     910                  mp.setThresholds(1, 2, 4)
     911                  mp.word_separators = u'[ \t]+'
     912                  gmPhraseWheel.cPhraseWheel.__init__ (
     913                          self,
     914                          *args,
     915                          **kwargs
     916                  )
     917                  self.matcher = mp
     918                  self.SetToolTipString(_('Select the type of address.'))
     919  #               self.capitalisation_mode = gmTools.CAPS_FIRST
     920                  self.selection_only = True
     921          #--------------------------------------------------------
     922  #       def GetData(self, can_create=False):
     923  #               if self.data is None:
     924  #                       if can_create:
     925  #                               self.data = gmDocuments.create_document_type(self.GetValue().strip())['pk_doc_type']    # FIXME: error handling
     926  #               return self.data
     927
     928  #============================================================
     929 -class cAddressMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
     930
     931 -        def __init__(self):
     932
     933                  query = u"""
     934  SELECT * FROM (
     935          (SELECT
     936                  pk_address AS data,
     937                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     938                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     939                                  || postcode || ', '
     940                                  || code_country
     941                  ) AS field_label,
     942                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     943                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     944                                  || postcode || ', '
     945                                  || l10n_state || ', '
     946                                  || l10n_country
     947                                  || coalesce(', ' || notes_street, '')
     948                                  || coalesce(', ' || notes_subunit, '')
     949                  ) AS list_label
     950          FROM
     951                  dem.v_address
     952          WHERE
     953                  street %(fragment_condition)s
     954
     955          ) UNION (
     956
     957          SELECT
     958                  pk_address AS data,
     959                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     960                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     961                                  || postcode || ', '
     962                                  || code_country
     963                  ) AS field_label,
     964                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     965                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     966                                  || postcode || ', '
     967                                  || l10n_state || ', '
     968                                  || l10n_country
     969                                  || coalesce(', ' || notes_street, '')
     970                                  || coalesce(', ' || notes_subunit, '')
     971                  ) AS list_label
     972          FROM
     973                  dem.v_address
     974          WHERE
     975                  postcode_street %(fragment_condition)s
     976
     977          ) UNION (
     978
     979          SELECT
     980                  pk_address AS data,
     981                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     982                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     983                                  || postcode || ', '
     984                                  || code_country
     985                  ) AS field_label,
     986                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
     987                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
     988                                  || postcode || ', '
     989                                  || l10n_state || ', '
     990                                  || l10n_country
     991                                  || coalesce(', ' || notes_street, '')
     992                                  || coalesce(', ' || notes_subunit, '')
     993                  ) AS list_label
     994          FROM
     995                  dem.v_address
     996          WHERE
     997                  postcode_urb %(fragment_condition)s
     998          )
     999  ) AS matching_addresses
    1000  ORDER BY list_label
    1001  LIMIT 50"""
    1002
    1003                  gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = query)
    1004
    1005                  self.setThresholds(2, 4, 6)
    1006  #               self.word_separators = u'[ \t]+'
    1007
    1008                  self._SQL_data2match = u"""
    1009          SELECT
    1010                  pk_address AS data,
    1011                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
    1012                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
    1013                                  || postcode || ', '
    1014                                  || code_country
    1015                  ) AS field_label,
    1016                  (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', '
    1017                                  || urb || coalesce(' (' || suburb || ')', '') || ', '
    1018                                  || postcode || ', '
    1019                                  || l10n_state || ', '
    1020                                  || l10n_country
    1021                                  || coalesce(', ' || notes_street, '')
    1022                                  || coalesce(', ' || notes_subunit, '')
    1023                  ) AS list_label
    1024          FROM
    1025                  dem.v_address
    1026          WHERE
    1027                  pk_address = %(pk)s
    1028          """
    1029
    1030  #============================================================
    1031 -class cAddressPhraseWheel(gmPhraseWheel.cPhraseWheel):
    1032
    1033 -        def __init__(self, *args, **kwargs):
    1034
    1035                  gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
    1036                  self.matcher = cAddressMatchProvider()
    1037                  self.SetToolTipString(_('Select an address by postcode or street name.'))
    1038                  self.selection_only = True
    1039                  self.__address = None
    1040                  self.__old_pk = None
    1041          #--------------------------------------------------------
    1042 -        def _get_data_tooltip(self):
    1043                  adr = self.address
    1044                  if adr is None:
    1045                          return None
    1046                  return u'\n'.join(adr.format())
    1047          #--------------------------------------------------------
    1048 -        def _data2instance(self):
    1049                  return gmDemographicRecord.cAddress(aPK_obj = self.GetData())
    1050          #--------------------------------------------------------
    1051          # properties
    1052          #--------------------------------------------------------
    1053 -        def __get_address(self):
    1054                  pk = self.GetData()
    1055                  if pk is None:
    1056                          self.__address = None
    1057                          return None
    1058                  if self.__address is None:
    1059                          self.__old_pk = pk
    1060                          self.__address = gmDemographicRecord.cAddress(aPK_obj = pk)
    1061                  else:
    1062                          if pk != self.__old_pk:
    1063                                  self.__old_pk = pk
    1064                                  self.__address = gmDemographicRecord.cAddress(aPK_obj = pk)
    1065                  return self.__address
    1066
    1067 -        def __set_address(self, address):
    1068                  if address is None:
    1069                          self.__old_pk = None
    1070                          self.__address = None
    1071                          self.SetText(u'', None)
    1072                          return
    1073                  if isinstance(address, gmDemographicRecord.cAddress):
    1074                          self.__old_pk = address['pk_address']
    1075                          self.__address = address
    1076                          pk = self.__old_pk
    1077                  else:
    1078                          self.__old_pk = None
    1079                          self.__address = None
    1080                          pk = address
    1081                  match = self.matcher.get_match_by_data(data = pk)
    1082                  if match is None:
    1083                          raise ValueError(u'[%s]: cannot match address [#%s]' % (self.__class__.__name__, pk))
    1084                  self.SetText(match['field_label'], pk)
    1085
    1086          address = property(__get_address, __set_address)
    1087
    1088  #================================================================
    1089  # main
    1090  #----------------------------------------------------------------
    1091  if __name__ == '__main__':
    1092
    1093          if len(sys.argv) < 2:
    1094                  sys.exit()
    1095
    1096          if sys.argv[1] != 'test':
    1097                  sys.exit()
    1098
    1099          from Gnumed.pycommon import gmI18N
    1100          gmI18N.activate_locale()
    1101          gmI18N.install_domain()
    1102          from Gnumed.business import gmPersonSearch
    1103
    1104          #--------------------------------------------------------
    1105 -        def test_country_prw():
    1106                  app = wx.PyWidgetTester(size = (200, 50))
    1107                  pw = cCountryPhraseWheel(app.frame, -1)
    1108                  app.frame.Show(True)
    1109                  app.MainLoop()
    1110          #--------------------------------------------------------
    1111 -        def test_state_prw():
    1112                  app = wx.PyWidgetTester(size = (200, 50))
    1113                  pw = cStateSelectionPhraseWheel(app.frame, -1)
    1114                  pw.set_context(context = u'zip', val = u'04318')
    1115                  pw.set_context(context = u'country', val = u'Deutschland')
    1116                  app.frame.Show(True)
    1117                  app.MainLoop()
    1118          #--------------------------------------------------------
    1119 -        def test_zipcode_prw():
    1120                  app = wx.PyWidgetTester(size = (200, 50))
    1121                  pw = cZipcodePhraseWheel(app.frame, -1)
    1122                  app.frame.Show(True)
    1123                  app.MainLoop()
    1124          #--------------------------------------------------------
    1125 -        def test_street_prw():
    1126                  app = wx.PyWidgetTester(size = (200, 50))
    1127                  pw = cStreetPhraseWheel(app.frame, -1)
    1128  #               pw.set_context(context = u'zip', val = u'04318')
    1129                  app.frame.Show(True)
    1130                  app.MainLoop()
    1131          #--------------------------------------------------------
    1132 -        def test_suburb_prw():
    1133                  app = wx.PyWidgetTester(size = (200, 50))
    1134                  pw = cSuburbPhraseWheel(app.frame, -1)
    1135                  app.frame.Show(True)
    1136                  app.MainLoop()
    1137          #--------------------------------------------------------
    1138 -        def test_urb_prw():
    1139                  app = wx.PyWidgetTester(size = (200, 50))
    1140                  pw = cUrbPhraseWheel(app.frame, -1)
    1141                  app.frame.Show(True)
    1142                  pw.set_context(context = u'zip', val = u'04317')
    1143                  app.MainLoop()
    1144          #--------------------------------------------------------
    1145 -        def test_address_type_prw():
    1146                  app = wx.PyWidgetTester(size = (200, 50))
    1147                  pw = cAddressTypePhraseWheel(app.frame, -1)
    1148                  app.frame.Show(True)
    1149                  app.MainLoop()
    1150          #--------------------------------------------------------
    1151 -        def test_address_prw():
    1152                  app = wx.PyWidgetTester(size = (200, 50))
    1153                  pw = cAddressPhraseWheel(app.frame, -1)
    1154                  app.frame.Show(True)
    1155                  app.MainLoop()
    1156          #--------------------------------------------------------
    1157 -        def test_address_ea_pnl():
    1158                  app = wx.PyWidgetTester(size = (600, 400))
    1159                  app.SetWidget(cAddressEditAreaPnl, address = gmDemographicRecord.cAddress(aPK_obj = 1))
    1160                  app.MainLoop()
    1161          #--------------------------------------------------------
    1162          #test_address_type_prw()
    1163          #test_zipcode_prw()
    1164          #test_state_prw()
    1165          #test_street_prw()
    1166          #test_suburb_prw()
    1167          #test_country_prw()
    1168          #test_urb_prw()
    1169          #test_address_ea_pnl()
    1170          test_address_prw()
    1171
    1172  #================================================================
    1173