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