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

# Source Code for Module Gnumed.business.gmOrganization

       1  """Organization classes
       2
       3  author: Karsten Hilbert et al
       4  """
       5  #============================================================
       6  __license__ = "GPL"
       7
       8
       9  import sys, logging
      10
      11
      12  if __name__ == '__main__':
      13          sys.path.insert(0, '../../')
      14  from Gnumed.pycommon import gmPG2
      15  from Gnumed.pycommon import gmBusinessDBObject
      16
      17  from Gnumed.business import gmDemographicRecord
      18
      19
      20  _log = logging.getLogger('gm.org')
      21  #============================================================
      22  # organization API
      23  #------------------------------------------------------------
      24  _SQL_get_org = u'SELECT * FROM dem.v_orgs WHERE %s'
      25
      26 -class cOrg(gmBusinessDBObject.cBusinessDBObject):
      27
      28          _cmd_fetch_payload = _SQL_get_org % u'pk_org = %s'
      29          _cmds_store_payload = [
      30                  u"""UPDATE dem.org SET
      31                                  description = %(organization)s,
      32                                  fk_category = %(pk_category_org)s,
      33                          WHERE
      34                                  pk = %(pk_org)s
      35                                          AND
      36                                  xmin = %(xmin_org)s
      37                          RETURNING
      38                                  xmin AS xmin_org"""
      39          ]
      40          _updatable_fields = [
      41                  u'organization',
      42                  u'pk_category_org'
      43          ]
      44  #------------------------------------------------------------
      45 -def org_exists(organization=None, category=None):
      46          args = {'desc': organization, 'cat': category}
      47
      48          if isinstance(category, basestring):
      49                  cat_part = u'fk_category = (SELECT pk FROM dem.org_category WHERE description = %(cat)s)'
      50          elif category is None:
      51                  cat_part = u'True'
      52          else:
      53                  cat_part = u'fk_category = %(cat)s'
      54
      55          cmd = u'SELECT pk FROM dem.org WHERE description = %%(desc)s AND %s' % cat_part
      56          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
      57          if len(rows) > 0:
      58                  return cOrg(aPK_obj = rows[0][0])
      59
      60          return None
      61  #------------------------------------------------------------
      62 -def create_org(organization=None, category=None):
      63
      64          org = org_exists(organization, category)
      65          if org is not None:
      66                  return org
      67
      68          args = {'desc': organization, 'cat': category}
      69
      70          if isinstance(category, basestring):
      71                  cat_part = u'(SELECT pk FROM dem.org_category WHERE description = %(cat)s)'
      72          else:
      73                  cat_part = u'%(cat)s'
      74
      75          cmd = u'INSERT INTO dem.org (description, fk_category) VALUES (%%(desc)s, %s) RETURNING pk' % cat_part
      76          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True)
      77
      78          return cOrg(aPK_obj = rows[0][0])
      79  #------------------------------------------------------------
      80 -def delete_org(organization=None):
      81          args = {'pk': organization}
      82          cmd = u"""
      83                  DELETE FROM dem.org
      84                  WHERE
      85                          pk = %(pk)s
      86                          AND NOT EXISTS (
      87                                  SELECT 1 FROM dem.org_unit WHERE fk_org = %(pk)s
      88                          )
      89          """
      90          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
      91          return True
      92  #------------------------------------------------------------
      93 -def get_orgs(order_by=None):
      94
      95          if order_by is None:
      96                  order_by = u''
      97          else:
      98                  order_by = u'ORDER BY %s' % order_by
      99
     100          cmd = _SQL_get_org % (u'TRUE %s' % order_by)
     101          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
     102
     103          return [ cOrg(row = {'data': r, 'idx': idx, 'pk_field': u'pk_org'}) for r in rows ]
     104
     105  #============================================================
     106  # organizational units API
     107  #------------------------------------------------------------
     108  _SQL_get_org_unit = u'SELECT * FROM dem.v_org_units WHERE %s'
     109
     110 -class cOrgUnit(gmBusinessDBObject.cBusinessDBObject):
     111
     112          _cmd_fetch_payload = _SQL_get_org_unit % u'pk_org_unit = %s'
     113          _cmds_store_payload = [
     114                  u"""UPDATE dem.org_unit SET
     115                                  description = %(unit)s,
     116                                  fk_org = %(pk_org)s,
     117                                  fk_category = %(pk_category_unit)s,
     118                                  fk_address = %(pk_address)s
     119                          WHERE
     120                                  pk = %(pk_org_unit)s
     121                                          AND
     122                                  xmin = %(xmin_org_unit)s
     123                          RETURNING
     124                                  xmin AS xmin_org_unit"""
     125          ]
     126          _updatable_fields = [
     127                  u'unit',
     128                  u'pk_org',
     129                  u'pk_category_unit',
     130                  u'pk_address'
     131          ]
     132          #--------------------------------------------------------
     133          # comms API
     134          #--------------------------------------------------------
     135 -        def get_comm_channels(self, comm_medium=None):
     136
     137                  args = {'pk': self.pk_obj, 'medium': comm_medium}
     138
     139                  if comm_medium is None:
     140                          cmd = u"""
     141                                  SELECT *
     142                                  FROM dem.v_org_unit_comms
     143                                  WHERE
     144                                          pk_org_unit = %(pk)s
     145                          """
     146                  else:
     147                          cmd = u"""
     148                                  SELECT *
     149                                  FROM dem.v_org_unit_comms
     150                                  WHERE
     151                                          pk_org_unit = %(pk)s
     152                                                  AND
     153                                          comm_type = %(medium)s
     154                          """
     155                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
     156
     157                  return [ gmDemographicRecord.cOrgCommChannel(row = {
     158                                          'pk_field': 'pk_lnk_org_unit2comm',
     159                                          'data': r,
     160                                          'idx': idx
     161                                  }) for r in rows
     162                          ]
     163          #--------------------------------------------------------
     164 -        def link_comm_channel(self, comm_medium=None, url=None, is_confidential=False, pk_channel_type=None):
     165                  """Link a communication medium with this org unit.
     166
     167                  @param comm_medium The name of the communication medium.
     168                  @param url The communication resource locator.
     169                  @type url A types.StringType instance.
     170                  @param is_confidential Wether the data must be treated as confidential.
     171                  @type is_confidential A types.BooleanType instance.
     172                  """
     173                  return gmDemographicRecord.create_comm_channel (
     174                          comm_medium = comm_medium,
     175                          url = url,
     176                          is_confidential = is_confidential,
     177                          pk_channel_type = pk_channel_type,
     178                          pk_org_unit = self.pk_obj
     179                  )
     180          #--------------------------------------------------------
     181 -        def unlink_comm_channel(self, comm_channel=None):
     182                  gmDemographicRecord.delete_comm_channel (
     183                          pk = comm_channel['pk_lnk_org_unit2comm'],
     184                          pk_org_unit = self.pk_obj
     185                  )
     186          #--------------------------------------------------------
     187          # address API
     188          #--------------------------------------------------------
     189 -        def link_address(self, id_type=None, address=None):
     190                  self.address = address
     191          #--------------------------------------------------------
     192 -        def unlink_address(self, address=None, pk_address=None):
     193                  """Remove an address from the org unit.
     194
     195                  The address itself stays in the database.
     196                  The address can be either cAdress or cPatientAdress.
     197                  """
     198                  self.address = None
     199          #--------------------------------------------------------
     200 -        def format(self, with_address=False, with_org=True):
     201                  lines = []
     202                  lines.append(_('Unit: %s (%s)') % (
     203                          self._payload[self._idx['unit']],
     204                          self._payload[self._idx['l10n_unit_category']]
     205                  ))
     206                  if with_org:
     207                          lines.append(_('Organization: %s (%s)') % (
     208                                  self._payload[self._idx['organization']],
     209                                  self._payload[self._idx['l10n_organization_category']]
     210                          ))
     211                  if with_address:
     212                          adr = self.address
     213                          if adr is not None:
     214                                  lines.extend(adr.format())
     215                  return lines
     216          #--------------------------------------------------------
     217          # properties
     218          #--------------------------------------------------------
     219 -        def _get_address(self):
     220                  if self._payload[self._idx['pk_address']] is None:
     221                          return None
     222                  return gmDemographicRecord.cAddress(aPK_obj = self._payload[self._idx['pk_address']])
     223
     224 -        def _set_address(self, address):
     225                  self['pk_address'] = address['pk_address']
     226                  self.save()
     227
     228          address = property(_get_address, _set_address)
     229  #------------------------------------------------------------
     230 -def create_org_unit(pk_organization=None, unit=None):
     231
     232          args = {'desc': unit, 'pk_org': pk_organization}
     233
     234          # exists ?
     235          cmd = u'SELECT pk FROM dem.org_unit WHERE description = %(desc)s AND fk_org = %(pk_org)s'
     236          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
     237          if len(rows) > 0:
     238                  return cOrgUnit(aPK_obj = rows[0][0])
     239
     240          # no, create
     241          cmd = u'INSERT INTO dem.org_unit (description, fk_org) VALUES (%(desc)s, %(pk_org)s) RETURNING pk'
     242          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True)
     243
     244          return cOrgUnit(aPK_obj = rows[0][0])
     245  #------------------------------------------------------------
     246 -def delete_org_unit(unit=None):
     247          args = {'pk': unit}
     248          cmd = u"DELETE FROM dem.org_unit WHERE pk = %(pk)s"
     249          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
     250          return True
     251  #------------------------------------------------------------
     252 -def get_org_units(order_by=None, org=None):
     253
     254          if order_by is None:
     255                  order_by = u''
     256          else:
     257                  order_by = u' ORDER BY %s' % order_by
     258
     259          if org is None:
     260                  where_part = u'TRUE'
     261          else:
     262                  where_part = u'pk_org = %(org)s'
     263
     264          args = {'org': org}
     265          cmd = (_SQL_get_org_unit % where_part) + order_by
     266          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
     267
     268          return [ cOrgUnit(row = {'data': r, 'idx': idx, 'pk_field': u'pk_org_unit'}) for r in rows ]
     269
     270  #======================================================================
     271  # main
     272  #----------------------------------------------------------------------
     273  if __name__ == "__main__":
     274
     275          if len(sys.argv) < 2:
     276                  sys.exit()
     277
     278          if sys.argv[1] != u'test':
     279                  sys.exit()
     280
     281
     282          for unit in get_org_units():
     283                  print unit
     284
     285          sys.exit(0)
     286  #============================================================
     287  #============================================================
     288  # outdated code below =======================================
     289  #============================================================
     290  #============================================================
     291  #============================================================
     292  #============================================================
     293  #============================================================
     294  #============================================================
     295  #============================================================
     296  #============================================================
     297  #============================================================
     298 -def get_comm_channels_data_for_org_ids( idList):
     299          """gets comm_channels for a list of org_id.
     300          returns a map keyed by org_id with lists of comm_channel data (url, type).
     301          this allows a single fetch of comm_channel data for multiple orgs"""
     302
     303          ids = ", ".join( [ str(x) for x in idList])
     304          cmd = """select l.id_org, id_type, url
     305                          from dem.comm_channel c, dem.lnk_org2comm_channel l
     306                          where
     307                                  c.id = l.id_comm and
     308                                  l.id_org in ( select id from dem.org where id in (%s) )
     309                  """ % ids
     310          result = gmPG.run_ro_query("personalia", cmd)
     311          if result == None:
     312                  _log.error("Unable to load comm channels for org" )
     313                  return None
     314          m = {}
     315          for (id_org, id_type, url) in result:
     316                  if not m.has_key(id_org):
     317                          m[id_org] = []
     318                  m[id_org].append( (id_type, url) )
     319
     320          return m # is a Map[id_org] = list of comm_channel data
     321
     322 -def get_address_data_for_org_ids( idList):
     323          """gets addresses for a list of valid id values for orgs.
     324          returns a map keyed by org_id with the address data
     325          """
     326
     327          ids = ", ".join( [ str(x) for x in idList])
     328          cmd = """select l.id_org, number, street, city, postcode, state, country
     329                          from dem.v_basic_address v , dem.lnk_org2address l
     330                                  where v.addr_id = l.id_address and
     331                                  l.id_org in ( select id from dem.org where id in (%s) ) """ % ids
     332          result = gmPG.run_ro_query( "personalia", cmd)
     333
     334          if result == None:
     335                  _log.error("failure in org address load" )
     336                  return None
     337          m = {}
     338          for (id_org, n,s,ci,p,st,co) in result:
     339                  m[id_org] =  (n,s,ci,p,st,co)
     340          return m
     341
     342 -def get_org_data_for_org_ids(idList):
     343          """ for a given list of org id values ,
     344                  returns a map of id_org vs. org attributes: description, id_category"""
     345
     346          ids = ", ".join( [ str(x) for x in idList])
     347          cmd = """select id, description, id_category  from dem.org
     348                          where id in ( select id from dem.org where id in( %s) )""" % ids
     349          #<DEBUG>
     350          print cmd
     351          #</DEBUG>
     352          result = gmPG.run_ro_query("personalia", cmd, )
     353          if result is None:
     354                  _log.error("Unable to load orgs with ids (%s)" %ids)
     355                  return None
     356          m = {}
     357          for (id_org, d, id_cat) in result:
     358                  m[id_org] = (d, id_cat)
     359          return m
     360  #============================================================
     361  #
     362  #  IGNORE THE FOLLOWING, IF NOT INTERESTED IN TEST CODE
     363  #
     364  #
     365
     366  if __name__ == '__main__':
     367          print "Please enter a write-enabled user e.g. _test-doc "
     368
     369 -        def testListOrgs():
     370                  print "running test listOrg"
     371                  for (f,a) in get_test_data():
     372                          h = cOrgImpl1()
     373                          h.set(*f)
     374                          h.setAddress(*a)
     375                          if not h.save():
     376                                  print "did not save ", f
     377
     378                  orgs = cOrgHelperImpl1().findAllOrganizations()
     379
     380                  for org in orgs:
     381                          print "Found org ", org.get(), org.getAddress()
     382                          if not org.shallow_del():
     383                                  print "Unable to delete above org"
     384
     385
     386
     387
     388
     389
     390 -        def get_test_data():
     391                  """test org data for unit testing in testOrg()"""
     392                  return [
     393                                  ( ["Box Hill Hospital", "", "", "Eastern", "hospital", "0398953333", "111-1111","bhh@oz", ""],  ["33", "Nelson Rd", "Box Hill", "3128", None , None] ),
     394                                  ( ["Frankston Hospital", "", "", "Peninsula", "hospital", "0397847777", "03784-3111","fh@oz", ""],  ["21", "Hastings Rd", "Frankston", "3199", None , None] )
     395                          ]
     396
     397 -        def get_test_persons():
     398                  return { "Box Hill Hospital":
     399                                  [
     400                                  ['Dr.', 'Bill' , 'Smith', '123-4567', '0417 111 222'],
     401                                  ['Ms.', 'Anita', 'Jones', '124-5544', '0413 222 444'],
     402                                  ['Dr.', 'Will', 'Stryker', '999-4444', '0402 333 111']  ],
     403                          "Frankston Hospital":
     404                                  [ [ "Dr.", "Jason", "Boathead", "444-5555", "0403 444 2222" ],
     405                                  [ "Mr.", "Barnie", "Commuter", "222-1111", "0444 999 3333"],
     406                                  [ "Ms.", "Morita", "Traveller", "999-1111", "0222 333 1111"]] }
     407
     408 -        def testOrgPersons():
     409                  m = get_test_persons()
     410                  d  = dict(  [  (f[0] , (f, a)) for (f, a) in get_test_data() ] )
     411                  for orgName , personList in m.items():
     412                          f1 , a1 = d[orgName][0], d[orgName][1]
     413                          _testOrgClassPersonRun( f1, a1, personList, cOrgImpl1)
     414                          _testOrgClassPersonRun( f1, a1, personList, cCompositeOrgImpl1)
     415                          _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl1().create)
     416                          _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl2().create)
     417                          _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl3().create)
     418                          _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl3().create, getTestIdentityUsing_cOrgDemographicAdapter)
     419
     420
     421 -        def _outputPersons( org):
     422                  m = org.getPersonMap()
     423
     424                  if m== []:
     425                          print "NO persons were found unfortunately"
     426
     427                  print """ TestOrgPersonRun got back for """
     428                  a = org.getAddress()
     429                  print org["name"], a["number"], a["street"], a["urb"], a["postcode"] , " phone=", org['phone']
     430
     431                  for id, r in m.items():
     432                          print "\t",", ".join( [ " ".join(r.get_names().values()),
     433                                                  "work no=", r.getCommChannel(gmDemographicRecord.WORK_PHONE),
     434                                                  "mobile no=", r.getCommChannel(gmDemographicRecord.MOBILE)
     435                                                  ] )
     436
     437
     438 -        def _testOrgPersonRun(f1, a1, personList):
     439                  print "Using test data :f1 = ", f1, "and a1 = ", a1 , " and lp = ", personList
     440                  print "-" * 50
     441                  _testOrgClassPersonRun( f1, a1, personList, cOrgImpl1)
     442                  _testOrgClassPersonRun( f1, a1, personList, cCompositeOrgImpl1)
     443                  _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl1().create)
     444                  _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl2().create)
     445
     446
     447 -        def _setIdentityTestData(identity, data):
     448                  identity.addName(data[1], data[2], True)
     449                  identity.setTitle(data[0])
     450                  identity.linkCommChannel( gmDemographicRecord.WORK_PHONE, data[3])
     451                  identity.linkCommChannel( gmDemographicRecord.MOBILE, data[4])
     452
     453 -        def getTestIdentityUsingDirectDemographicRecord( data, org):
     454                  id = gmPerson.create_dummy_identity()
     455                  identity = gmDemographicRecord.cDemographicRecord_SQL(id)
     456                  _setIdentityTestData(identity, data)
     457                  return identity
     458
     459 -        def getTestIdentityUsing_cOrgDemographicAdapter( data, org):
     460                  helper = cOrgHelperImpl3()
     461                  orgPerson= helper.createOrgPerson()
     462                  orgPerson.setParent(org)
     463                  orgPerson['name'] = ' '.join( [data[0], data[1], data[2]])
     464                  orgPerson['phone'] = data[3]
     465                  orgPerson['mobile'] = data[4]
     466                  orgPerson.save()
     467                  return orgPerson.getDemographicRecord()
     468
     469
     470 -        def _testOrgClassPersonRun(f1, a1, personList, orgCreate, identityCreator = getTestIdentityUsingDirectDemographicRecord):
     471                  print "-" * 50
     472                  print "Testing org creator ", orgCreate
     473                  print " and identity creator ", identityCreator
     474                  print "-" * 50
     475                  h = orgCreate()
     476                  h.set(*f1)
     477                  h.setAddress(*a1)
     478                  if not h.save():
     479                          print "Unable to save org for person test"
     480                          h.shallow_del()
     481                          return False
     482                  # use gmDemographicRecord to convert person list
     483                  for lp in personList:
     484                          identity = identityCreator(lp, h)
     485                          result , msg = h.linkPerson(identity)
     486                          print msg
     487
     488                  _outputPersons(h)
     489                  deletePersons(h)
     490
     491                  if h.shallow_del():
     492                          print "Managed to dispose of org"
     493                  else:
     494                          print "unable to dispose of org"
     495
     496                  return True
     497
     498  #       def testOrgPerson(f1, a1, personList):
     499
     500 -        def deletePerson(id):
     501                  cmds = [ ( "delete from dem.lnk_identity2comm_chan where fk_identity=%d"%id,[]),
     502                          ("delete from dem.names where id_identity=%d"%id,[]),
     503                          ("delete from dem.identity where id = %d"%id,[]) ]
     504                  result = gmPG.run_commit("personalia", cmds)
     505                  return result
     506
     507 -        def deletePersons( org):
     508                  map = org.getPersonMap()
     509                  for id, r in map.items():
     510                          org.unlinkPerson(r)
     511
     512                          result = deletePerson(r.getID())
     513                          if result == None:
     514                                  _log.error("FAILED TO CLEANUP PERSON %d" %r.getID() )
     515
     516
     517
     518 -        def testOrg():
     519                  """runs a test of load, save , shallow_del  on items in from get_test_data"""
     520                  l = get_test_data()
     521                  results = []
     522                  for (f, a) in l:
     523                          result, obj =   _testOrgRun(f, a)
     524                          results.append( (result, obj) )
     525                  return results
     526
     527
     528
     529 -        def _testOrgRun( f1, a1):
     530
     531                  print """testing single level orgs"""
     532                  f = [ "name", "office", "subtype",  "memo", "category", "phone", "fax", "email","mobile"]
     533                  a = ["number", "street", "urb", "postcode", "state", "country"]
     534                  h = cOrgImpl1()
     535
     536                  h.set(*f1)
     537                  h.setAddress(*a1)
     538
     539                  print "testing get, getAddress"
     540                  print h.get()
     541                  print h.getAddressDict()
     542
     543                  import sys
     544                  if not  h.save():
     545                          print "failed to save first time. Is an old test org needing manual removal?"
     546                          return False, h
     547                  print "saved pk =", h.getId()
     548
     549
     550                  pk = h.getId()
     551                  if h.shallow_del():
     552                          print "shallow deleted ", h['name']
     553                  else:
     554                          print "failed shallow delete of ", h['name']
     555
     556
     557
     558                  h2 = cOrgImpl1()
     559
     560                  print "testing load"
     561
     562                  print "should fail"
     563                  if not h2.load(pk):
     564                          print "Failed as expected"
     565
     566                  if h.save():
     567                          print "saved ", h['name'] , "again"
     568                  else:
     569                          print "failed re-save"
     570                          return False, h
     571
     572                  h['fax'] = '222-1111'
     573                  print "using update save"
     574
     575                  if h.save():
     576                          print "saved updated passed"
     577                          print "Test reload next"
     578                  else:
     579                          print "failed save of updated data"
     580                          print "continuing to reload"
     581
     582
     583                  if not h2.load(h.getId()):
     584                          print "failed load"
     585                          return False, h
     586                  print "reloaded values"
     587                  print h2.get()
     588                  print h2.getAddressDict()
     589
     590                  print "** End of Test org"
     591
     592                  if h2.shallow_del():
     593                          print "cleaned up"
     594                  else:
     595                          print "Test org needs to be manually removed"
     596
     597                  return True, h2
     598
     599 -        def clean_test_org():
     600                  l = get_test_data()
     601
     602                  names = [ "".join( ["'" ,str(org[0]), "'"] )  for ( org, address) in l]
     603                  names += [ "'John Hunter Hospital'", "'Belmont District Hospital'"]
     604                  nameList = ",".join(names)
     605                  categoryList = "'hospital'"
     606
     607                  cmds = [ ( """create temp table del_org as
     608                                  select id  from dem.org
     609                                  where description in(%s) or
     610                                  id_category in ( select id from dem.org_category c
     611                                                          where c.description in (%s))
     612                          """ % (nameList, categoryList), [] ),
     613                          ("""create temp table del_identity as
     614                          select id  from dem.identity
     615                          where id in
     616                                  (
     617                                          select id_identity from dem.lnk_person_org_address
     618                                          where id_org in ( select id from del_org)
     619                                  )""",[] ),
     620                          ("""create temp table del_comm as
     621                          (select id_comm from dem.lnk_org2comm_channel where
     622                                  id_org in ( select id from del_org)
     623                          ) UNION
     624                          (select id_comm from dem.lnk_identity2comm_chan where
     625                                  id_identity in ( select id from del_identity)
     626                          )""", [] ),
     627                          ("""delete from dem.names where id_identity in
     628                                          (select id from del_identity)""",[]),
     629                          ("""delete from dem.lnk_person_org_address where
     630                                          id_org in (select id from del_org )""",[]),
     631                          ("""delete from dem.lnk_person_org_address where
     632                                          id_identity in (select id from del_identity)""", []),
     633                          ("""delete from dem.lnk_org2comm_channel
     634                          where id_org in (select id from del_org) """,[]),
     635                          ("""delete from dem.lnk_identity2comm_chan
     636                                          where id_identity in (select id from del_identity)""",[] ),
     637                          ("""delete from dem.comm_channel where id in ( select id_comm from del_comm)""",[]),
     638                          ("""delete from dem.lnk_job2person where id_identity in (select id from del_identity)""", []),
     639                          ("""delete from dem.identity where id in (select id from del_identity)""",[] ),
     640                          ("""delete from dem.org where id in ( select id from del_org) """ , [] ),
     641                          ("""drop table del_comm""",[]),
     642                          ("""drop table del_identity""",[]),
     643                          ("""drop table del_org""", [])
     644
     645                          ]
     646                  result =  gmPG.run_commit("personalia", cmds) <> None
     647
     648                  return result
     649
     650
     651 -        def login_user_and_test(logintest, service = 'personalia', msg = "failed test" , use_prefix_rw= False):
     652                  """ tries to get and verify a read-write connection
     653                  which has permission to write to org tables, so the test case
     654                  can run.
     655                  """
     656                  login2 = gmPG.request_login_params()
     657
     658                  #login as the RW user
     659                  p = gmPG.ConnectionPool( login2)
     660                  if use_prefix_rw:
     661                          conn = p.GetConnection( service, readonly = 0)
     662                  else:
     663                          conn = p.GetConnection(service)
     664                  result = logintest(conn)
     665
     666                  if result is False:
     667                          print msg
     668
     669                  p.ReleaseConnection(service)
     670                  return result, login2
     671
     672 -        def test_rw_user(conn):
     673                  # test it is a RW user, by making a entry and deleting it
     674                  try:
     675                          c.reload("org_category")
     676                          cursor = conn.cursor()
     677
     678                          cursor.execute("select last_value from dem.org_id_seq")
     679                          [org_id_seq] = cursor.fetchone()
     680
     681                          cursor.execute("""
     682                  insert into dem.org ( description, id_category, id)
     683                  values ( 'xxxDEFAULTxxx', %d,
     684                  %d)
     685                          """ % ( c.getId('org_category', 'hospital') , org_id_seq + 1 ) )
     686                          cursor.execute("""
     687                  delete from dem.org where id = %d""" % ( org_id_seq + 1) )
     688                  # make sure this exercise is committed, else a deadlock will occur
     689                          conn.commit()
     690                  except:
     691                          _log.exception("Test of Update Permission failed")
     692                          return False
     693                  return True
     694
     695 -        def test_admin_user(conn):
     696                  try:
     697                          cursor = conn.cursor()
     698
     699                          cursor.execute("select last_value from dem.org_category_id_seq")
     700                          [org_cat_id_seq] = cursor.fetchone()
     701
     702                          cursor.execute("""
     703                  insert into dem.org_category ( description, id)
     704                  values ( 'xxxDEFAULTxxx',%d)
     705                          """ %   (org_cat_id_seq + 1 ) )
     706                          cursor.execute("""
     707                  delete from dem.org_category where description like 'xxxDEFAULTxxx' """ )
     708                  # make sure this exercise is committed, else a deadlock will occur
     709                          conn.commit()
     710                  except:
     711                          _log.exception("Test of Update Permission failed")
     712                          return False
     713                  return True
     714
     715 -        def login_rw_user():
     716                  return  login_user_and_test( test_rw_user, "login cannot update org", use_prefix_rw = True)
     717
     718
     719 -        def login_admin_user():
     720                  return  login_user_and_test( test_admin_user, "login cannot update org_category" )
     721
     722
     723 -        def create_temp_categories( categories = ['hospital']):
     724                  print "NEED TO CREATE TEMPORARY ORG_CATEGORY.\n\n ** PLEASE ENTER administrator login  : e.g  user 'gm-dbo' and  his password"
     725                  #get a admin login
     726                  for i in xrange(0, 4):
     727                          result ,tmplogin = login_admin_user()
     728                          if result:
     729                                  break
     730                  if i == 4:
     731                          print "Failed to login"
     732                          return categories
     733
     734                  # and save it , for later removal of test categories.
     735                  from Gnumed.pycommon import gmLoginInfo
     736                  adminlogin = gmLoginInfo.LoginInfo(*tmplogin.GetInfo())
     737
     738                  #login as admin
     739                  p = gmPG.ConnectionPool( tmplogin)
     740                  conn = p.GetConnection("personalia")
     741
     742                  # use the last value + 1 of the relevant sequence, but don't increment it
     743                  cursor = conn.cursor()
     744
     745                  failed_categories = []
     746                  n =1
     747                  for cat in categories:
     748                          cursor.execute("select last_value from dem.org_category_id_seq")
     749                          [org_cat_id_seq] = cursor.fetchone()
     750
     751                          cursor.execute( "insert into dem.org_category(description, id) values('%s', %d)" % (cat, org_cat_id_seq + n) )
     752                          cursor.execute("select id from dem.org_category where description in ('%s')" % cat)
     753
     754                          result =  cursor.fetchone()
     755                          if result == None or len(result) == 0:
     756                                  failed_categories.append(cat)
     757                                  print "Failed insert of category", cat
     758                                  conn.rollback()
     759                          else:
     760                                  conn.commit()
     761                          n += 1
     762
     763                  conn.commit()
     764                  p.ReleaseConnection('personalia')
     765                  return failed_categories, adminlogin
     766
     767 -        def clean_org_categories(adminlogin = None, categories = ['hospital'], service='personalia'):
     768
     769                  print"""
     770
     771                  The temporary category(s) will now
     772                  need to be removed under an administrator login
     773                  e.g. gm-dbo
     774                  Please enter login for administrator:
     775                  """
     776                  if adminlogin is None:
     777                          for i in xrange(0, 4):
     778                                  result, adminlogin = login_admin_user()
     779                                  if  result:
     780                                          break
     781                          if i == 4:
     782                                  print "FAILED TO LOGIN"
     783                                  return categories
     784
     785                  p = gmPG.ConnectionPool(adminlogin)
     786                  conn = p.GetConnection(service)
     787                  failed_remove = []
     788                  for cat in categories:
     789                          try:
     790                                  cursor = conn.cursor()
     791                                  cursor.execute( "delete from  dem.org_category where description in ('%s')"%cat)
     792                                  conn.commit()
     793                                  cursor.execute("select id from dem.org_category where description in ('%s')"%cat)
     794                                  if cursor.fetchone() == None:
     795                                          print "Succeeded in removing temporary org_category"
     796                                  else:
     797                                          print "*** Unable to remove temporary org_category"
     798                                          failed_remove .append(cat)
     799                          except:
     800                                  import sys
     801                                  print sys.exc_info()[0], sys.exc_info()[1]
     802                                  import traceback
     803                                  traceback.print_tb(sys.exc_info()[2])
     804
     805                                  failed_remove.append(cat)
     806
     807                  conn = None
     808                  p.ReleaseConnection(service)
     809                  if failed_remove <> []:
     810                          print "FAILED TO REMOVE ", failed_remove
     811                  return failed_remove
     812
     813 -        def test_CatFinder():
     814                  print "TESTING cCatFinder"
     815
     816                  print """c = cCatFinder("org_category")"""
     817                  c = cCatFinder("org_category")
     818
     819                  print c.getCategories("org_category")
     820
     821                  print """c = cCatFinder("enum_comm_types")"""
     822                  c = cCatFinder("enum_comm_types")
     823
     824                  l = c.getCategories("enum_comm_types")
     825                  print "testing getId()"
     826                  l2 = []
     827                  for x in l:
     828                          l2.append((x, c.getId("enum_comm_types", x)))
     829                  print l2
     830
     831                  print """testing borg behaviour of cCatFinder"""
     832
     833                  print c.getCategories("org_category")
     834
     835
     836 -        def help():
     837                  print """\nNB If imports not found , try:
     838
     839                  change to gnumed/client directory , then
     840
     841                  export PYTHONPATH=$PYTHONPATH:../;python business/gmOrganization.py
     842
     843                          --clean , cleans the test data and categories
     844
     845                          --gui   sets up as for no arguments, then runs the client.
     846                                  on normal exit of client, normal tests run, and
     847                                  then cleanup of entered data.
     848
     849                          using the gui,
     850
     851                          the 'list organisations' toolbar button , loads all organisations
     852                          in the database, and display suborgs and persons associated
     853                          with each organisation.
     854
     855                          the 'add organisation' button will add a top-level organisation.
     856                          the 'add branch/division' button will work when the last selected
     857                          org was a top level org.
     858
     859                          the 'add person M|F' button works if an org is selected.
     860
     861                          the save button works when entry is finished.
     862
     863                          selecting on an item, will bring it into the editing area.
     864
     865                          No test yet for dirtied edit data, to query whether to
     866                          save or discard. (30/5/2004)
     867                  """
     868                  print
     869                  print "In the connection query, please enter"
     870                  print "a WRITE-ENABLED user e.g. _test-doc (not test-doc), and the right password"
     871                  print
     872                  print "Run the unit test with cmdline argument '--clean'  if trying to clean out test data"
     873                  print
     874
     875                  print """You can get a sermon by running
     876                  export PYTHONPATH=$PYTHONPATH:../;python business/gmOrganization.py --sermon
     877                  """
     878                  print """
     879                  Pre-requisite data in database is :
     880                  gnumed=# select * from org_category ;
     881                  id | description
     882                  ----+-------------
     883                  1 | hospital
     884                  (1 row)
     885
     886                  gnumed=# select * from enum_comm_types ;
     887                  id | description
     888                  ----+-------------
     889                  1 | email
     890                  2 | fax
     891                  3 | homephone
     892                  4 | workphone
     893                  5 | mobile
     894                  6 | web
     895                  7 | jabber
     896                  (7 rows)
     897                  """
     898
     899 -        def sermon():
     900                                  print"""
     901                  This test case shows how many things can go wrong , even with just a test case.
     902                  Problem areas include:
     903                  - postgres administration :  pg_ctl state, pg_hba.conf, postgres.conf  config files .
     904                  - schema integrity constraints : deletion of table entries which are subject to foreign keys, no input for no default value and no null value columns, input with duplicated values where unique key constraint applies to non-primary key columns, dealing with access control by connection identity management.
     905
     906
     907                  - efficiency trade-offs -e.g. using db objects for localising code with data and easier function call interface ( then hopefully, easier to program with) , vs. need to access many objects at once
     908                  without calling the backend for each object.
     909
     910                  - error and exception handling - at what point in the call stack to handle an error.
     911                  Better to use error return values and log exceptions near where they occur, vs. wrapping inside try: except: blocks and catching typed exceptions.
     912
     913
     914                  - test-case construction:  test data is needed often, and the issue
     915                  is whether it is better to keep the test data volatile in the test-case,
     916                  which handles both its creation and deletion, or to add it to test data
     917                  server configuration files, which may involve running backend scripts
     918                  for loading and removing test data.
     919
     920
     921
     922                  - Database connection problems:
     923                  -Is the problem in :
     924                          - pg_ctl start -D  /...mydata-directory is wrong, and gnumed isn't existing there.
     925
     926                          - ..mydata-directory/pg_hba.conf
     927                                  - can psql connect locally and remotely with the username and password.
     928                                  - Am I using md5 authenentication and I've forgotten the password.
     929                                          - I need to su postgres, alter pg_hba.conf to use trust for
     930                                          the gnumed database, pg_ctl restart -D .., su normal_user,  psql gnumed, alter user my_username password 'doh'
     931                                          - might be helpful: the default password for _test-doc is test-doc
     932
     933                          - ../mydata-directory/postgres.conf
     934                                  - tcp connect  flag isn't set to true
     935
     936                          - remote/local mixup :
     937                          a different set of user passwords on different hosts. e.g the password
     938                          for _test-doc is 'pass' on localhost and 'test-doc' for the serverhost.
     939                          - In the prompts for admin and user login, local host was used for one, and
     940                          remote host for the other
     941
     942
     943
     944                  - test data won't go away :
     945                  - 'hospital' category in org_category : the test case failed in a previous run
     946                  and the test data was left there; now the test case won't try to delete it
     947                  because it exists as a pre-existing category;
     948                          soln : run with  --clean  option
     949
     950                  - test-case failed unexpectedly, or break key was hit in the middle of a test-case run.
     951                          Soln: run with --clean option,
     952
     953
     954                  """
     955
     956
     957  #============================================================
     958
     959          import sys
     960          testgui = False
     961          if len(sys.argv) > 1:
     962                  if sys.argv[1] == '--clean':
     963                          result = clean_test_org()
     964                          p = gmPG.ConnectionPool()
     965                          p.ReleaseConnection('personalia')
     966                          if result:
     967                                  print "probably succeeded in cleaning orgs"
     968                          else:   print "failed to clean orgs"
     969
     970                          clean_org_categories()
     971                          sys.exit(1)
     972
     973                  if sys.argv[1] == "--sermon":
     974                          sermon()
     975
     976                  if sys.argv[1] == "--help":
     977                          help()
     978
     979                  if sys.argv[1] =="--gui":
     980                          testgui = True
     981
     982          print "*" * 50
     983          print "RUNNING UNIT TEST of gmOrganization "
     984
     985
     986          test_CatFinder()
     987          tmp_category = False  # tmp_category means test data will need to be added and removed
     988                          # for  org_category .
     989
     990          c = cCatFinder()
     991          if not "hospital" in c.getCategories("org_category") :
     992                  print "FAILED in prerequisite for org_category : test categories are not present."
     993
     994                  tmp_category = True
     995
     996          if tmp_category:
     997                  # test data in a categorical table (restricted access) is needed
     998
     999                  print """You will need to switch login identity to database administrator in order
    1000                          to have permission to write to the org_category table,
    1001                          and then switch back to the ordinary write-enabled user in order
    1002                          to run the test cases.
    1003                          Finally you will need to switch back to administrator login to
    1004                          remove the temporary org_categories.
    1005                          """
    1006                  categories = ['hospital']
    1007                  result, adminlogin = create_temp_categories(categories)
    1008                  if result == categories:
    1009                          print "Unable to create temporary org_category. Test aborted"
    1010                          sys.exit(-1)
    1011                  if result <> []:
    1012                          print "UNABLE TO CREATE THESE CATEGORIES"
    1013                          if not raw_input("Continue ?") in ['y', 'Y'] :
    1014                                  sys.exit(-1)
    1015
    1016          try:
    1017                  results = []
    1018                  if tmp_category:
    1019                                  print "succeeded in creating temporary org_category"
    1020                                  print
    1021                                  print "** Now ** RESUME LOGIN **  of write-enabled user (e.g. _test-doc) "
    1022                                  while (1):
    1023                                          # get the RW user for org tables (again)
    1024                                          if login_rw_user():
    1025                                                  break
    1026
    1027                  if testgui:
    1028                          if cCatFinder().getId('org_category','hospital') == None:
    1029                                  print "Needed to set up temporary org_category 'hospital"
    1030                                  sys.exit(-1)
    1031                          import os
    1032                          print os.environ['PWD']
    1033                          os.spawnl(os.P_WAIT, "/usr/bin/python", "/usr/bin/python","wxpython/gnumed.py", "--debug")
    1034
    1035                          #os.popen2('python client/wxpython/gnumed.py --debug')
    1036
    1037                          # run the test case
    1038                  results = testOrg()
    1039
    1040                          # cleanup after the test case
    1041                  for (result , org) in results:
    1042                          if not result and org.getId() <> None:
    1043                                  print "trying cleanup"
    1044                                  if  org.shallow_del(): print "  may have succeeded"
    1045                                  else:
    1046                                          print "May need manual removal of org id =", org.getId()
    1047
    1048                  testOrgPersons()
    1049
    1050                  testListOrgs()
    1051
    1052          except:
    1053                  import  sys
    1054                  print sys.exc_info()[0], sys.exc_info()[1]
    1055                  _log.exception( "Fatal exception")
    1056
    1057                  # clean-up any temporary categories.
    1058          if tmp_category:
    1059                  try:
    1060                          clean_org_categories(adminlogin)
    1061                  except:
    1062                          while(not login_rw_user()[0]):
    1063                                  pass
    1064                          clean_test_org()
    1065                          clean_org_categories(adminlogin)
    1066
    1067
    1068
    1069 -def setPostcodeWidgetFromUrbId(postcodeWidget, id_urb):
    1070           """convenience method for urb and postcode phrasewheel interaction.
    1071              never called without both arguments, but need to check that id_urb
    1072              is not invalid"""
    1073           #TODO type checking that the postcodeWidget is a phrasewheel configured
    1074           # with a postcode matcher
    1075           if  postcodeWidget is None or id_urb is None:
    1076                   return False
    1077           postcode = getPostcodeForUrbId(id_urb)
    1078           if postcode is None:
    1079                   return False
    1080           if len(postcode) == 0:
    1081                   return True
    1082           postcodeWidget.SetValue(postcode)
    1083           postcodeWidget.input_was_selected= 1
    1084           return True
    1085
    1086   #------------------------------------------------------------
    1087
    1088 -def setUrbPhraseWheelFromPostcode(pwheel, postcode):
    1089           """convenience method for common postcode to urb phrasewheel collaboration.
    1090              there is no default args for these utility functions,
    1091              This function is never called without both arguments, otherwise
    1092              there is no intention (= modify the urb phrasewheel with postcode value).
    1093           """
    1094           # TODO type checking that the pwheel is a urb phrasewheel with a urb matcher
    1095           # clearing post code unsets target
    1096           # phrasewheel's postcode context
    1097           if pwheel is None:
    1098                   return False
    1099           if postcode == '':
    1100                   pwheel.set_context("postcode", "%")
    1101                   return True
    1102           urbs = getUrbsForPostcode(postcode)
    1103           if urbs is None:
    1104                   return False
    1105           if len(urbs) == 0:
    1106                   return True
    1107           pwheel.SetValue(urbs[0])
    1108           pwheel.input_was_selected = 1
    1109
    1110           # FIXME: once the postcode context is set,
    1111           # the urb phrasewheel will only return urbs with
    1112           # the same postcode. These can be viewed by clearing
    1113           # the urb widget. ?How to unset the postcode context,
    1114           # some gui gesture ? clearing the postcode
    1115           # (To view all the urbs for a set context,
    1116           # put a "*" in the urb box and activate the picklist.
    1117           # THE PROBLEM WITH THIS IS IF THE USER CLEARS THE BOX AND SET CONTEXT IS RESET,
    1118           # then the "*" will try to pull all thousands of urb names, freezing the app.
    1119           # so needs a fixup (? have SQL select ... LIMIT n in Phrasewheel )
    1120
    1121           pwheel.set_context("postcode", postcode)
    1122           return True
    1123
    1124  #======================================================================
    1125