Package Gnumed :: Package business :: Module gmDemographicRecord |
[frames] | no frames]

# Source Code for Module Gnumed.business.gmDemographicRecord

       1  # -*- coding: utf8 -*-
       2  """GNUmed demographics object.
       3
       4  This is a patient object intended to let a useful client-side
       5  API crystallize from actual use in true XP fashion.
       6
       7  license: GPL v2 or later
       8  """
       9  #============================================================
      10  __version__ = "$Revision: 1.106 $"
      11  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>, I.Haywood <ihaywood@gnu.org>"
      12
      13  # stdlib
      14  import sys
      15  import os
      16  import os.path
      17  import logging
      18
      19
      20  # GNUmed
      21  if __name__ == '__main__':
      22          sys.path.insert(0, '../../')
      23  from Gnumed.pycommon import gmDispatcher
      24  from Gnumed.pycommon import gmBusinessDBObject
      25  from Gnumed.pycommon import gmPG2
      26  from Gnumed.pycommon import gmTools
      27
      28
      29  _log = logging.getLogger('gm.business')
      30  _log.info(__version__)
      31
      32  try:
      33          _
      34  except NameError:
      35          _ = lambda x:x
      36
      37  #============================================================
      38  # occupation handling
      39  #------------------------------------------------------------
      40 -def get_occupations(pk_identity=None):
      41          cmd = u"""
      42                  SELECT *
      43                  FROM dem.v_person_jobs
      44                  WHERE pk_identity = %(pk)s
      45                  ORDER BY l10n_occupation
      46          """
      47          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': pk_identity}}])
      48          return rows
      49  #============================================================
      50  # text+image tags
      51  #------------------------------------------------------------
      52  _SQL_get_tag_image = u"SELECT * FROM ref.v_tag_images_no_data WHERE %s"
      53
      54 -class cTagImage(gmBusinessDBObject.cBusinessDBObject):
      55
      56          _cmd_fetch_payload = _SQL_get_tag_image % u"pk_tag_image = %s"
      57          _cmds_store_payload = [
      58                  u"""
      59                          UPDATE ref.tag_image SET
      60                                  description = gm.nullify_empty_string(%(description)s),
      61                                  filename = gm.nullify_empty_string(%(filename)s)
      62                          WHERE
      63                                  pk = %(pk_tag_image)s
      64                                          AND
      65                                  xmin = %(xmin_tag_image)s
      66                          RETURNING
      67                                  pk as pk_tag_image,
      68                                  xmin as xmin_tag_image
      69                  """
      70          ]
      71          _updatable_fields = [u'description', u'filename']
      72          #--------------------------------------------------------
      73 -        def export_image2file(self, aChunkSize=0, filename=None):
      74
      75                  if self._payload[self._idx['size']] == 0:
      76                          return None
      77
      78                  if filename is None:
      79                          suffix = None
      80                          # preserve original filename extension if available
      81                          if self._payload[self._idx['filename']] is not None:
      82                                  name, suffix = os.path.splitext(self._payload[self._idx['filename']])
      83                                  suffix = suffix.strip()
      84                                  if suffix == u'':
      85                                          suffix = None
      86                          # get unique filename
      87                          filename = gmTools.get_unique_filename (
      88                                  prefix = 'gm-tag_image-',
      89                                  suffix = suffix
      90                          )
      91
      92                  success = gmPG2.bytea2file (
      93                          data_query = {
      94                                  'cmd': u'SELECT substring(image from %(start)s for %(size)s) FROM ref.tag_image WHERE pk = %(pk)s',
      95                                  'args': {'pk': self.pk_obj}
      96                          },
      97                          filename = filename,
      98                          chunk_size = aChunkSize,
      99                          data_size = self._payload[self._idx['size']]
     100                  )
     101
     102                  if success:
     103                          return filename
     104
     105                  return None
     106          #--------------------------------------------------------
     107 -        def update_image_from_file(self, filename=None):
     108                  # sanity check
     109                  if not (os.access(filename, os.R_OK) and os.path.isfile(filename)):
     110                          _log.error('[%s] is not a readable file' % filename)
     111                          return False
     112
     113                  gmPG2.file2bytea (
     114                          query = u"UPDATE ref.tag_image SET image = %(data)s::bytea WHERE pk = %(pk)s",
     115                          filename = filename,
     116                          args = {'pk': self.pk_obj}
     117                  )
     118
     119                  # must update XMIN now ...
     120                  self.refetch_payload()
     121                  return True
     122  #------------------------------------------------------------
     123 -def get_tag_images(order_by=None):
     124          if order_by is None:
     125                  order_by = u'true'
     126          else:
     127                  order_by = 'true ORDER BY %s' % order_by
     128
     129          cmd = _SQL_get_tag_image % order_by
     130          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
     131          return [ cTagImage(row = {'data': r, 'idx': idx, 'pk_field': 'pk_tag_image'}) for r in rows ]
     132  #------------------------------------------------------------
     133 -def create_tag_image(description=None, link_obj=None):
     134
     135          args = {u'desc': description, u'img': u''}
     136          cmd = u"""
     137                  INSERT INTO ref.tag_image (
     138                          description,
     139                          image
     140                  ) VALUES (
     141                          %(desc)s,
     142                          %(img)s::bytea
     143                  )
     144                  RETURNING pk
     145          """
     146          rows, idx = gmPG2.run_rw_queries (
     147                  link_obj = link_obj,
     148                  queries = [{'cmd': cmd, 'args': args}],
     149                  end_tx = True,
     150                  return_data = True,
     151                  get_col_idx = False
     152          )
     153
     154          return cTagImage(aPK_obj = rows[0]['pk'])
     155  #------------------------------------------------------------
     156 -def delete_tag_image(tag_image=None):
     157          args = {'pk': tag_image}
     158          cmd = u"""
     159                  DELETE FROM ref.tag_image
     160                  WHERE
     161                          pk = %(pk)s
     162                                  AND
     163                          NOT EXISTS (
     164                                  SELECT 1
     165                                  FROM dem.identity_tag
     166                                  WHERE fk_tag = %(pk)s
     167                                  LIMIT 1
     168                          )
     169                  RETURNING 1
     170          """
     171          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
     172          if len(rows) == 0:
     173                  return False
     174          return True
     175
     176  #============================================================
     177  _SQL_get_identity_tags = u"""SELECT * FROM dem.v_identity_tags WHERE %s"""
     178
     179 -class cIdentityTag(gmBusinessDBObject.cBusinessDBObject):
     180
     181          _cmd_fetch_payload = _SQL_get_identity_tags % u"pk_identity_tag = %s"
     182          _cmds_store_payload = [
     183                  u"""
     184                          UPDATE dem.identity_tag SET
     185                                  fk_tag = %(pk_tag_image)s,
     186                                  comment = gm.nullify_empty_string(%(comment)s)
     187                          WHERE
     188                                  pk = %(pk_identity_tag)s
     189                                          AND
     190                                  xmin = %(xmin_identity_tag)s
     191                          RETURNING
     192                                  pk as pk_identity_tag,
     193                                  xmin as xmin_identity_tag
     194                  """
     195          ]
     196          _updatable_fields = [u'fk_tag', u'comment']
     197          #--------------------------------------------------------
     198 -        def export_image2file(self, aChunkSize=0, filename=None):
     199
     200                  if self._payload[self._idx['image_size']] == 0:
     201                          return None
     202
     203                  if filename is None:
     204                          suffix = None
     205                          # preserve original filename extension if available
     206                          if self._payload[self._idx['filename']] is not None:
     207                                  name, suffix = os.path.splitext(self._payload[self._idx['filename']])
     208                                  suffix = suffix.strip()
     209                                  if suffix == u'':
     210                                          suffix = None
     211                          # get unique filename
     212                          filename = gmTools.get_unique_filename (
     213                                  prefix = 'gm-identity_tag-',
     214                                  suffix = suffix
     215                          )
     216
     217                  exported = gmPG2.bytea2file (
     218                          data_query = {
     219                                  'cmd': u'SELECT substring(image from %(start)s for %(size)s) FROM ref.tag_image WHERE pk = %(pk)s',
     220                                  'args': {'pk': self._payload[self._idx['pk_tag_image']]}
     221                          },
     222                          filename = filename,
     223                          chunk_size = aChunkSize,
     224                          data_size = self._payload[self._idx['image_size']]
     225                  )
     226                  if exported:
     227                          return filename
     228
     229                  return None
     230  #============================================================
     231  #============================================================
     232 -def get_countries():
     233          cmd = u"""
     234                  select
     235                          _(name) as l10n_country, name, code, deprecated
     236                  from dem.country
     237                  order by l10n_country"""
     238          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
     239          return rows
     240  #============================================================
     241 -def get_country_for_region(region=None):
     242          cmd = u"""
     243  SELECT code_country, l10n_country FROM dem.v_state WHERE l10n_state = %(region)s
     244          union
     245  SELECT code_country, l10n_country FROM dem.v_state WHERE state = %(region)s
     246  """
     247          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'region': region}}])
     248          return rows
     249  #============================================================
     250 -def delete_province(province=None, delete_urbs=False):
     251
     252          args = {'prov': province}
     253
     254          queries = []
     255          if delete_urbs:
     256                  queries.append ({
     257                          'cmd': u"""
     258                                  delete from dem.urb du
     259                                  where
     260                                          du.id_state = %(prov)s
     261                                                  and
     262                                          not exists (select 1 from dem.street ds where ds.id_urb = du.id)""",
     263                          'args': args
     264                  })
     265
     266          queries.append ({
     267                  'cmd': u"""
     268                          delete from dem.state ds
     269                          where
     270                                  ds.id = %(prov)s
     271                                          and
     272                                  not exists (select 1 from dem.urb du where du.id_state = ds.id)""",
     273                  'args': args
     274          })
     275
     276          gmPG2.run_rw_queries(queries = queries)
     277
     278          return True
     279  #------------------------------------------------------------
     280 -def create_province(name=None, code=None, country=None):
     281
     282          args = {'code': code, 'country': country, 'name': name}
     283
     284          cmd = u"""SELECT EXISTS (SELECT 1 FROM dem.state WHERE name = %(name)s)"""
     285          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
     286
     287          if rows[0][0]:
     288                  return
     289
     290          cmd = u"""
     291                  INSERT INTO dem.state (
     292                          code, country, name
     293                  ) VALUES (
     294                          %(code)s, %(country)s, %(name)s
     295                  )"""
     296          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
     297  #------------------------------------------------------------
     298 -def get_provinces():
     299          cmd = u"""
     300                  select
     301                          l10n_state, l10n_country, state, code_state, code_country, pk_state, country_deprecated
     302                  from dem.v_state
     303                  order by l10n_country, l10n_state"""
     304          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
     305          return rows
     306  #============================================================
     307  # address related classes
     308  #------------------------------------------------------------
     309 -class cAddress(gmBusinessDBObject.cBusinessDBObject):
     310          """A class representing an address as an entity in itself.
     311
     312          We consider addresses to be self-complete "labels" for locations.
     313          It does not depend on any people actually living there. Thus
     314          an address can get attached to as many people as we want to
     315          signify that that is their place of residence/work/...
     316
     317          This class acts on the address as an entity. Therefore it can
     318          modify the address fields. Think carefully about *modifying*
     319          addresses attached to people, though. Most times when you think
     320          person.modify_address() what you *really* want is as sequence of
     321          person.unlink_address(old) and person.link_address(new).
     322
     323          Modifying an address may or may not be the proper thing to do as
     324          it will transparently modify the address for *all* the people to
     325          whom it is attached. In many cases you will want to create a *new*
     326          address and link it to a person instead of the old address.
     327          """
     328          _cmd_fetch_payload = u"select * from dem.v_address where pk_address = %s"
     329          _cmds_store_payload = [
     330                  u"""UPDATE dem.address SET
     331                                  aux_street = %(notes_street)s,
     332                                  subunit = %(subunit)s,
     333                                  addendum = %(notes_subunit)s,
     334                                  lat_lon = %(lat_lon_street)s
     335                          WHERE
     336                                  id = %(pk_address)s
     337                                          AND
     338                                  xmin = %(xmin_address)s
     339                          RETURNING
     340                                  xmin AS xmin_address"""
     341          ]
     342          _updatable_fields = [
     343                  'notes_street',
     344                  'subunit',
     345                  'notes_subunit',
     346                  'lat_lon_address'
     347          ]
     348          #--------------------------------------------------------
     349 -        def format(self):
     350                  return format_address(address = self)
     351  #------------------------------------------------------------
     352 -def address_exists(country=None, state=None, urb=None, postcode=None, street=None, number=None, subunit=None):
     353
     354          cmd = u"""SELECT dem.address_exists(%(country)s, %(state)s, %(urb)s, %(postcode)s, %(street)s, %(number)s, %(subunit)s)"""
     355          args = {
     356                  'country': country,
     357                  'state': state,
     358                  'urb': urb,
     359                  'postcode': postcode,
     360                  'street': street,
     361                  'number': number,
     362                  'subunit': subunit
     363          }
     364
     365          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
     366          if rows[0][0] is None:
     367                  _log.debug('address does not exist')
     368                  for key, val in args.items():
     369                          _log.debug('%s: %s', key, val)
     370                  return None
     371
     372          return rows[0][0]
     373  #------------------------------------------------------------
     374 -def create_address(country=None, state=None, urb=None, suburb=None, postcode=None, street=None, number=None, subunit=None):
     375
     376          if suburb is not None:
     377                  suburb = gmTools.none_if(suburb.strip(), u'')
     378
     379          pk_address = address_exists (
     380                  country = country,
     381                  state = state,
     382                  urb = urb,
     383  #               suburb = suburb,
     384                  postcode = postcode,
     385                  street = street,
     386                  number = number,
     387                  subunit = subunit
     388          )
     389          if pk_address is not None:
     390                  return cAddress(aPK_obj=pk_address)
     391
     392          cmd = u"""
     393  SELECT dem.create_address (
     394          %(number)s,
     395          %(street)s,
     396          %(postcode)s,
     397          %(urb)s,
     398          %(state)s,
     399          %(country)s,
     400          %(subunit)s
     401  )"""
     402          args = {
     403                  'number': number,
     404                  'street': street,
     405                  'postcode': postcode,
     406                  'urb': urb,
     407                  'state': state,
     408                  'country': country,
     409                  'subunit': subunit
     410          }
     411          queries = [{'cmd': cmd, 'args': args}]
     412
     413          rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
     414          adr = cAddress(aPK_obj = rows[0][0])
     415
     416          if suburb is not None:
     417                  queries = [{
     418                          # CAVE: suburb will be ignored if there already is one
     419                          'cmd': u"update dem.street set suburb = %(suburb)s where id=%(pk_street)s and suburb is Null",
     420                          'args': {'suburb': suburb, 'pk_street': adr['pk_street']}
     421                  }]
     422                  rows, idx = gmPG2.run_rw_queries(queries = queries)
     423
     424          return adr
     425  #------------------------------------------------------------
     426 -def delete_address(pk_address=None):
     427          cmd = u"""
     428                  DELETE FROM dem.address
     429                  WHERE
     430                          id = %(pk)s
     431                                  AND
     432                          NOT EXISTS ((
     433                                  SELECT 1 FROM dem.org_unit WHERE fk_address = %(pk)s LIMIT 1
     434                                          ) UNION (
     435                                  SELECT 1 FROM dem.lnk_identity2comm WHERE fk_address = %(pk)s LIMIT 1
     436                                          ) UNION (
     437                                  SELECT 1 FROM dem.lnk_person_org_address WHERE id_address = %(pk)s LIMIT 1
     438                          ))
     439                  """
     440          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk_address}}])
     441          return True
     442  #------------------------------------------------------------
     443 -def format_address(address=None):
     444          data = {
     445                  'pk_adr': address['pk_address'],
     446                  'street': address['street'],
     447                  'notes_street': gmTools.coalesce(address['notes_street'], u'', u' (%s)'),
     448                  'number': address['number'],
     449                  'subunit': gmTools.coalesce(address['subunit'], u'', u'/%s'),
     450                  'notes_subunit': gmTools.coalesce(address['notes_subunit'], u'', u' (%s)'),
     451                  'zip': address['postcode'],
     452                  'urb': address['urb'],
     453                  'suburb': gmTools.coalesce(address['suburb'], u'', u' (%s)'),
     454                  'l10n_state': address['l10n_state'],
     455                  'code_state': address['code_state'],
     456                  'l10n_country': address['l10n_country'],
     457                  'code_country': address['code_country']
     458          }
     459          txt = _(
     460                  'Address [#%(pk_adr)s]\n'
     461                  ' Street: %(street)s%(notes_street)s\n'
     462                  ' Number/Unit: %(number)s%(subunit)s%(notes_subunit)s\n'
     463                  ' Location: %(zip)s %(urb)s%(suburb)s\n'
     464                  ' Region: %(l10n_state)s, %(code_state)s\n'
     465                  ' Country: %(l10n_country)s, %(code_country)s'
     466          ) % data
     467          return txt.split('\n')
     468  #------------------------------------------------------------
     469 -def get_address_types(identity=None):
     470          cmd = u'select id as pk, name, _(name) as l10n_name from dem.address_type'
     471          rows, idx = gmPG2.run_rw_queries(queries=[{'cmd': cmd}])
     472          return rows
     473  #------------------------------------------------------------
     474 -def get_addresses(order_by=None):
     475
     476          if order_by is None:
     477                  order_by = u''
     478          else:
     479                  order_by = u'ORDER BY %s' % order_by
     480
     481          cmd = u"SELECT * FROM dem.v_address %s" % order_by
     482          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
     483          return [ cAddress(row = {'data': r, 'idx': idx, 'pk_field': u'pk_address'}) for r in rows ]
     484
     485  #===================================================================
     486 -class cPatientAddress(gmBusinessDBObject.cBusinessDBObject):
     487
     488          _cmd_fetch_payload = u"SELECT * FROM dem.v_pat_addresses WHERE pk_address = %s"
     489          _cmds_store_payload = [
     490                  u"""UPDATE dem.lnk_person_org_address SET
     491                                  id_type = %(pk_address_type)s
     492                          WHERE
     493                                  id = %(pk_lnk_person_org_address)s
     494                                          AND
     495                                  xmin = %(xmin_lnk_person_org_address)s
     496                          RETURNING
     497                                  xmin AS xmin_lnk_person_org_address
     498                  """
     499          ]
     500          _updatable_fields = ['pk_address_type']
     501          #---------------------------------------------------------------
     502 -        def get_identities(self, same_lastname=False):
     503                  pass
     504          #--------------------------------------------------------
     505 -        def format(self):
     506                  txt = format_address(address = self)
     507                  txt.append(_(' Type: %s') % self._payload[self._idx['l10n_address_type']])
     508                  return txt
     509  #===================================================================
     510  # communication channels API
     511  #-------------------------------------------------------------------
     512 -class cCommChannel(gmBusinessDBObject.cBusinessDBObject):
     513
     514          _cmd_fetch_payload = u"SELECT * FROM dem.v_person_comms WHERE pk_lnk_identity2comm = %s"
     515          _cmds_store_payload = [
     516                  u"""UPDATE dem.lnk_identity2comm SET
     517                                  --fk_address = %(pk_address)s,
     518                                  fk_type = dem.create_comm_type(%(comm_type)s),
     519                                  url = %(url)s,
     520                                  is_confidential = %(is_confidential)s,
     521                                  comment = gm.nullify_empty_string(%(comment)s)
     522                          WHERE
     523                                  pk = %(pk_lnk_identity2comm)s
     524                                          AND
     525                                  xmin = %(xmin_lnk_identity2comm)s
     526                          RETURNING
     527                                  xmin AS xmin_lnk_identity2comm
     528                  """
     529          ]
     530          _updatable_fields = [
     531                  #'pk_address',
     532                  'url',
     533                  'comm_type',
     534                  'is_confidential',
     535                  'comment'
     536          ]
     537  #-------------------------------------------------------------------
     538 -class cOrgCommChannel(gmBusinessDBObject.cBusinessDBObject):
     539
     540          _cmd_fetch_payload = u"SELECT * FROM dem.v_org_unit_comms WHERE pk_lnk_org_unit2comm = %s"
     541          _cmds_store_payload = [
     542                  u"""UPDATE dem.lnk_org_unit2comm SET
     543                                  fk_type = dem.create_comm_type(%(comm_type)s),
     544                                  url = %(url)s,
     545                                  is_confidential = %(is_confidential)s
     546                          WHERE
     547                                  pk = %(pk_lnk_org_unit2comm)s
     548                                          AND
     549                                  xmin = %(xmin_lnk_org_unit2comm)s
     550                          RETURNING
     551                                  xmin AS xmin_lnk_org_unit2comm
     552                  """
     553          ]
     554          _updatable_fields = [
     555                  'url',
     556                  'comm_type',
     557                  'is_confidential'
     558          ]
     559  #-------------------------------------------------------------------
     560 -def create_comm_channel(comm_medium=None, url=None, is_confidential=False, pk_channel_type=None, pk_identity=None, pk_org_unit=None):
     561          """Create a communications channel for a patient."""
     562
     563          if url is None:
     564                  return None
     565
     566          args = {
     567                  'url': url,
     568                  'secret': is_confidential,
     569                  'pk_type': pk_channel_type,
     570                  'type': comm_medium
     571          }
     572
     573          if pk_identity is not None:
     574                  args['pk_owner'] = pk_identity
     575                  tbl = u'dem.lnk_identity2comm'
     576                  col = u'fk_identity'
     577                  view = u'dem.v_person_comms'
     578                  view_pk = u'pk_lnk_identity2comm'
     579                  channel_class = cCommChannel
     580          if pk_org_unit is not None:
     581                  args['pk_owner'] = pk_org_unit
     582                  tbl = u'dem.lnk_org_unit2comm'
     583                  col = u'fk_org_unit'
     584                  view = u'dem.v_org_unit_comms'
     585                  view_pk = u'pk_lnk_org_unit2comm'
     586                  channel_class = cOrgCommChannel
     587
     588          if pk_channel_type is None:
     589                  cmd = u"""INSERT INTO %s (
     590                          %s,
     591                          url,
     592                          fk_type,
     593                          is_confidential
     594                  ) VALUES (
     595                          %%(pk_owner)s,
     596                          %%(url)s,
     597                          dem.create_comm_type(%%(type)s),
     598                          %%(secret)s
     599                  )""" % (tbl, col)
     600          else:
     601                  cmd = u"""INSERT INTO %s (
     602                          %s,
     603                          url,
     604                          fk_type,
     605                          is_confidential
     606                  ) VALUES (
     607                          %%(pk_owner)s,
     608                          %%(url)s,
     609                          %%(pk_type)s,
     610                          %%(secret)s
     611                  )""" % (tbl, col)
     612
     613          queries = [{'cmd': cmd, 'args': args}]
     614          cmd = u"SELECT * FROM %s WHERE %s = currval(pg_get_serial_sequence('%s', 'pk'))" % (view, view_pk, tbl)
     615          queries.append({'cmd': cmd})
     616
     617          rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True, get_col_idx = True)
     618
     619          if pk_identity is not None:
     620                  return cCommChannel(row = {'pk_field': view_pk, 'data': rows[0], 'idx': idx})
     621
     622          return channel_class(row = {'pk_field': view_pk, 'data': rows[0], 'idx': idx})
     623  #-------------------------------------------------------------------
     624 -def delete_comm_channel(pk=None, pk_patient=None, pk_org_unit=None):
     625          if pk_patient is not None:
     626                  query = {
     627                          'cmd': u"DELETE FROM dem.lnk_identity2comm WHERE pk = %(pk)s AND fk_identity = %(pat)s",
     628                          'args': {'pk': pk, 'pat': pk_patient}
     629                  }
     630          if pk_org_unit is not None:
     631                  query = {
     632                          'cmd': u"DELETE FROM dem.lnk_org_unit2comm WHERE pk = %(pk)s AND fk_org_unit = %(unit)s",
     633                          'args': {'pk': pk, 'unit': pk_org_unit}
     634                  }
     635          gmPG2.run_rw_queries(queries = [query])
     636  #-------------------------------------------------------------------
     637 -def get_comm_channel_types():
     638          cmd = u"SELECT pk, _(description) AS l10n_description, description FROM dem.enum_comm_types"
     639          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
     640          return rows
     641  #-------------------------------------------------------------------
     642 -def delete_comm_channel_type(pk_channel_type=None):
     643          cmd = u"""
     644                  DELETE FROM dem.enum_comm_types
     645                  WHERE
     646                          pk = %(pk)s
     647                          AND NOT EXISTS (
     648                                  SELECT 1 FROM dem.lnk_identity2comm WHERE fk_type = %(pk)s
     649                          )
     650                          AND NOT EXISTS (
     651                                  SELECT 1 FROM dem.lnk_org_unit2comm WHERE fk_type = %(pk)s
     652                          )
     653          """
     654          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk_channel_type}}])
     655          return True
     656  #===================================================================
     657  #-------------------------------------------------------------------
     658
     659 -class cOrg (gmBusinessDBObject.cBusinessDBObject):
     660          """
     661          Organisations
     662
     663          This is also the common ancestor of cIdentity, self._table is used to
     664          hide the difference.
     665          The aim is to be able to sanely write code which doesn't care whether
     666          its talking to an organisation or an individual"""
     667          _table = "org"
     668
     669          _cmd_fetch_payload = "select *, xmin from dem.org where id=%s"
     670          _cmds_lock_rows_for_update = ["select 1 from dem.org where id=%(id)s and xmin=%(xmin)s"]
     671          _cmds_store_payload = [
     672                  """update dem.org set
     673                          description=%(description)s,
     674                          id_category=(select id from dem.org_category where description=%(occupation)s)
     675                  where id=%(id)s""",
     676                  "select xmin from dem.org where id=%(id)s"
     677          ]
     678          _updatable_fields = ["description", "occupation"]
     679          _service = 'personalia'
     680          #------------------------------------------------------------------
     681 -        def cleanup (self):
     682                  pass
     683          #------------------------------------------------------------------
     684 -        def export_demographics (self):
     685                  if not self.__cache.has_key ('addresses'):
     686                          self['addresses']
     687                  if not self.__cache.has_key ('comms'):
     688                          self['comms']
     689                  return self.__cache
     690          #--------------------------------------------------------------------
     691 -        def get_members (self):
     692                  """
     693                  Returns a list of (address dict, cIdentity) tuples
     694                  """
     695                  cmd = """select
     696                                  vba.id,
     697                                  vba.number,
     698                                  vba.addendum,
     699                                  vba.street,
     700                                  vba.urb,
     701                                  vba.postcode,
     702                                  at.name,
     703                                  lpoa.id_type,
     704                                  vbp.pk_identity,
     705                                  title,
     706                                  firstnames,
     707                                  lastnames,
     708                                  dob,
     709                                  cob,
     710                                  gender,
     711                                  pupic,
     712                                  pk_marital_status,
     713                                  marital_status,
     714                                  karyotype,
     715                                  xmin_identity,
     716                                  preferred
     717                          from
     718                                  dem.v_basic_address vba,
     719                                  dem.lnk_person_org_address lpoa,
     720                                  dem.address_type at,
     721                                  dem.v_basic_person vbp
     722                          where
     723                                  lpoa.id_address = vba.id
     724                                  and lpoa.id_type = at.id
     725                                  and lpoa.id_identity = vbp.pk_identity
     726                                  and lpoa.id_org = %%s
     727                                  """
     728
     729                  rows, idx = gmPG.run_ro_query('personalia', cmd, 1, self.getId ())
     730                  if rows is None:
     731                          return []
     732                  elif len(rows) == 0:
     733                          return []
     734                  else:
     735                          return [({'pk':i[0], 'number':i[1], 'addendum':i[2], 'street':i[3], 'city':i[4], 'postcode':i[5], 'type':i[6], 'id_type':i[7]}, cIdentity (row = {'data':i[8:], 'id':idx[8:], 'pk_field':'id'})) for i in rows]
     736          #------------------------------------------------------------
     737 -        def set_member (self, person, address):
     738                  """
     739                  Binds a person to this organisation at this address.
     740                  person is a cIdentity object
     741                  address is a dict of {'number', 'street', 'addendum', 'city', 'postcode', 'type'}
     742                  type is one of the IDs returned by getAddressTypes
     743                  """
     744                  cmd = "insert into dem.lnk_person_org_address (id_type, id_address, id_org, id_identity) values (%(type)s, dem.create_address (%(number)s, %(addendum)s, %(street)s, %(city)s, %(postcode)s), %(org_id)s, %(pk_identity)s)"
     745                  address['pk_identity'] = person['pk_identity']
     746                  address['org_id'] = self.getId()
     747                  if not id_addr:
     748                          return (False, None)
     749                  return gmPG.run_commit2 ('personalia', [(cmd, [address])])
     750          #------------------------------------------------------------
     751 -        def unlink_person (self, person):
     752                  cmd = "delete from dem.lnk_person_org_address where id_org = %s and id_identity = %s"
     753                  return gmPG.run_commit2 ('personalia', [(cmd, [self.getId(), person.ID])])
     754          #----------------------------------------------------------------------
     755 -        def getId (self):
     756                  """
     757                  Hide the difference between org.id and v_basic_person.pk_identity
     758                  """
     759                  return self['id']
     760  #==============================================================================
     761 -def get_time_tuple (mx):
     762          """
     763          wrap mx.DateTime brokenness
     764          Returns 9-tuple for use with pyhon time functions
     765          """
     766          return [ int(x) for x in  str(mx).split(' ')[0].split('-') ] + [0,0,0, 0,0,0]
     767  #----------------------------------------------------------------
     768 -def getAddressTypes():
     769          """Gets a dict matching address types to their ID"""
     770          row_list = gmPG.run_ro_query('personalia', "select name, id from dem.address_type")
     771          if row_list is None:
     772                  return {}
     773          if len(row_list) == 0:
     774                  return {}
     775          return dict (row_list)
     776  #----------------------------------------------------------------
     777 -def getMaritalStatusTypes():
     778          """Gets a dictionary matching marital status types to their internal ID"""
     779          row_list = gmPG.run_ro_query('personalia', "select name, pk from dem.marital_status")
     780          if row_list is None:
     781                  return {}
     782          if len(row_list) == 0:
     783                  return {}
     784          return dict(row_list)
     785  #------------------------------------------------------------------
     786 -def getRelationshipTypes():
     787          """Gets a dictionary of relationship types to internal id"""
     788          row_list = gmPG.run_ro_query('personalia', "select description, id from dem.relation_types")
     789          if row_list is None:
     790                  return None
     791          if len (row_list) == 0:
     792                  return None
     793          return dict(row_list)
     794
     795  #----------------------------------------------------------------
     796 -def getUrb (id_urb):
     797          cmd = """
     798  select
     799          dem.state.name,
     800          dem.urb.postcode
     801  from
     802          dem.urb,
     803          dem.state
     804  where
     805          dem.urb.id = %s and
     806          dem.urb.id_state = dem.state.id"""
     807          row_list = gmPG.run_ro_query('personalia', cmd, None, id_urb)
     808          if not row_list:
     809                  return None
     810          else:
     811                  return (row_list[0][0], row_list[0][1])
     812
     813 -def getStreet (id_street):
     814          cmd = """
     815  select
     816          dem.state.name,
     817          coalesce (dem.street.postcode, dem.urb.postcode),
     818          dem.urb.name
     819  from
     820          dem.urb,
     821          dem.state,
     822          dem.street
     823  where
     824          dem.street.id = %s and
     825          dem.street.id_urb = dem.urb.id and
     826          dem.urb.id_state = dem.state.id
     827  """
     828          row_list = gmPG.run_ro_query('personalia', cmd, None, id_street)
     829          if not row_list:
     830                  return None
     831          else:
     832                  return (row_list[0][0], row_list[0][1], row_list[0][2])
     833
     834 -def getCountry (country_code):
     835          row_list = gmPG.run_ro_query('personalia', "select name from dem.country where code = %s", None, country_code)
     836          if not row_list:
     837                  return None
     838          else:
     839                  return row_list[0][0]
     840  #-------------------------------------------------------------------------------
     841 -def get_town_data (town):
     842          row_list = gmPG.run_ro_query ('personalia', """
     843  select
     844          dem.urb.postcode,
     845          dem.state.code,
     846          dem.state.name,
     847          dem.country.code,
     848          dem.country.name
     849  from
     850          dem.urb,
     851          dem.state,
     852          dem.country
     853  where
     854          dem.urb.name = %s and
     855          dem.urb.id_state = dem.state.id and
     856          dem.state.country = dem.country.code""", None, town)
     857          if not row_list:
     858                  return (None, None, None, None, None)
     859          else:
     860                  return tuple (row_list[0])
     861  #============================================================
     862  # callbacks
     863  #------------------------------------------------------------
     864 -def _post_patient_selection(**kwargs):
     865          print "received post_patient_selection notification"
     866          print kwargs['kwds']
     867  #============================================================
     868
     869  #============================================================
     870  # main
     871  #------------------------------------------------------------
     872  if __name__ == "__main__":
     873
     874          if len(sys.argv) < 2:
     875                  sys.exit()
     876
     877          if sys.argv[1] != 'test':
     878                  sys.exit()
     879
     880          import random
     881          #--------------------------------------------------------
     882 -        def test_address_exists():
     883
     884                  addresses = [
     885                          {
     886                          'country': 'Germany',
     887                          'state': 'Sachsen',
     888                          'urb': 'Leipzig',
     889                          'postcode': '04318',
     890                          'street': u'Cunnersdorfer Strasse',
     891                          'number': '11'
     892                          },
     893                          {
     894                          'country': 'DE',
     895                          'state': 'SN',
     896                          'urb': 'Leipzig',
     897                          'postcode': '04317',
     898                          'street': u'Riebeckstraße',
     899                          'number': '65',
     900                          'subunit': 'Parterre'
     901                          },
     902                          {
     903                          'country': 'DE',
     904                          'state': 'SN',
     905                          'urb': 'Leipzig',
     906                          'postcode': '04317',
     907                          'street': u'Riebeckstraße',
     908                          'number': '65',
     909                          'subunit': '1. Stock'
     910                          },
     911                          {
     912                          'country': 'DE',
     913                          'state': 'SN',
     914                          'urb': 'Leipzig',
     915                          'postcode': '04317',
     916                          'street': u'Riebeckstraße',
     917                          'number': '65',
     918                          'subunit': '1. Stock'
     919                          },
     920                          {
     921  #                       'country': 'DE',
     922  #                       'state': 'SN',
     923                          'urb': 'Leipzig',
     924                          'postcode': '04317',
     925                          'street': u'Riebeckstraße',
     926                          'number': '65',
     927                          'subunit': '1. Stock'
     928                          },
     929                  ]
     930
     931                  for adr in addresses:
     932                          print adr
     933                          exists = address_exists(**adr)
     934                          if exists is None:
     935                                  print "address does not exist"
     936                          else:
     937                                  print "address exists, primary key:", exists
     938
     939          #--------------------------------------------------------
     940 -        def test_create_address():
     941                  address = create_address (
     942                          country ='DE',
     943                          state ='SN',
     944                          urb ='Leipzig',
     945                          suburb ='Sellerhausen',
     946                          postcode ='04318',
     947                          street = u'Cunnersdorfer Strasse',
     948                          number = '11'
     949  #                       ,notes_subunit = '4.Stock rechts'
     950                  )
     951                  print "created existing address"
     952                  print address.format()
     953
     954                  su = str(random.random())
     955
     956                  address = create_address (
     957                          country ='DE',
     958                          state = 'SN',
     959                          urb ='Leipzig',
     960                          suburb ='Sellerhausen',
     961                          postcode ='04318',
     962                          street = u'Cunnersdorfer Strasse',
     963                          number = '11',
     964  #                       notes_subunit = '4.Stock rechts',
     965                          subunit = su
     966                  )
     967                  print "created new address with subunit", su
     968                  print address
     969                  print address.format()
     970                  print "deleted address:", delete_address(pk_address = address['pk_address'])
     971          #--------------------------------------------------------
     972 -        def test_get_countries():
     973                  for c in get_countries():
     974                          print c
     975          #--------------------------------------------------------
     976 -        def test_get_country_for_region():
     977                  region = raw_input("Please enter a region: ")
     978                  print "country for region [%s] is: %s" % (region, get_country_for_region(region = region))
     979          #--------------------------------------------------------
     980 -        def test_delete_tag():
     981                  if delete_tag_image(tag_image = 9999):
     982                          print "deleted tag 9999"
     983                  else:
     984                          print "did not delete tag 9999"
     985                  if delete_tag_image(tag_image = 1):
     986                          print "deleted tag 1"
     987                  else:
     988                          print "did not delete tag 1"
     989          #--------------------------------------------------------
     990 -        def test_tag_images():
     991                  tag = cTagImage(aPK_obj = 1)
     992                  print tag
     993                  print get_tag_images()
     994          #--------------------------------------------------------
     995
     996          #gmPG2.get_connection()
     997
     998          #test_address_exists()
     999          test_create_address()
    1000          #test_get_countries()
    1001          #test_get_country_for_region()
    1002          #test_delete_tag()
    1003          #test_tag_images()
    1004
    1005          sys.exit()
    1006  #============================================================
    1007