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