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

Source Code for Module Gnumed.business.gmOrganization

   1  """Organization classes 
   2   
   3  author: Karsten Hilbert et al 
   4  """ 
   5  #============================================================ 
   6  __license__ = "GPL" 
   7   
   8   
   9  import sys, logging 
  10   
  11   
  12  if __name__ == '__main__': 
  13          sys.path.insert(0, '../../') 
  14  from Gnumed.pycommon import gmPG2 
  15  from Gnumed.pycommon import gmBusinessDBObject 
  16   
  17   
  18  _log = logging.getLogger('gm.org') 
  19  #============================================================ 
  20  # organization API 
  21  #------------------------------------------------------------ 
  22  _SQL_get_org = u'SELECT * FROM dem.v_orgs WHERE %s' 
  23   
24 -class cOrg(gmBusinessDBObject.cBusinessDBObject):
25 26 _cmd_fetch_payload = _SQL_get_org % u'pk_org = %s' 27 _cmds_store_payload = [ 28 u"""UPDATE dem.org SET 29 description = %(organization)s, 30 fk_category = %(pk_category_org)s, 31 WHERE 32 pk = %(pk_org)s 33 AND 34 xmin = %(xmin_org)s 35 RETURNING 36 xmin AS xmin_org""" 37 ] 38 _updatable_fields = [ 39 u'organization', 40 u'pk_category_org' 41 ]
42 #------------------------------------------------------------
43 -def create_org(organization=None, category=None):
44 45 args = {'desc': organization, 'cat': category} 46 cmd = u'INSERT INTO dem.org (description, fk_category) VALUES (%%(desc)s, %s) RETURNING pk' 47 if isinstance(category, basestring): 48 cmd = cmd % u'(SELECT pk FROM dem.org_category WHERE description = %(cat)s)' 49 else: 50 cmd = cmd % u'%(cat)s' 51 52 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True) 53 54 return cOrg(aPK_obj = rows[0][0])
55 #------------------------------------------------------------
56 -def get_orgs(order_by=None):
57 58 if order_by is None: 59 order_by = u'' 60 else: 61 order_by = u'ORDER BY %s' % order_by 62 63 cmd = _SQL_get_org % (u'TRUE %s' % order_by) 64 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 65 66 return [ cOrg(row = {'data': r, 'idx': idx, 'pk_field': u'pk_org'}) for r in rows ]
67 68 #============================================================ 69 # organizational units API 70 #------------------------------------------------------------ 71 _SQL_get_org_unit = u'SELECT * FROM dem.v_org_units WHERE %s' 72
73 -class cOrgUnit(gmBusinessDBObject.cBusinessDBObject):
74 75 _cmd_fetch_payload = _SQL_get_org_unit % u'pk_org_unit = %s' 76 _cmds_store_payload = [ 77 u"""UPDATE dem.org_unit SET 78 description = %(unit)s, 79 fk_org = %(pk_org)s, 80 fk_category = %(pk_category_unit)s, 81 fk_address = %(pk_address)s 82 WHERE 83 pk = %(pk_org_unit)s 84 AND 85 xmin = %(xmin_org_unit)s 86 RETURNING 87 xmin AS xmin_org_unit""" 88 ] 89 _updatable_fields = [ 90 u'unit', 91 u'pk_org', 92 u'pk_category_unit', 93 u'pk_address' 94 ]
95 96 #------------------------------------------------------------
97 -def create_org_unit(pk_organization=None, unit=None):
98 99 args = {'desc': unit, 'pk_org': pk_organization} 100 cmd = u'INSERT INTO dem.org_unit (description, fk_org) VALUES (%(desc)s, %(pk_org)s) RETURNING pk' 101 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True) 102 103 return cOrgUnit(aPK_obj = rows[0][0])
104 #------------------------------------------------------------
105 -def get_org_units(order_by=None):
106 107 if order_by is None: 108 order_by = u'' 109 else: 110 order_by = u'ORDER BY %s' % order_by 111 112 cmd = _SQL_get_org_unit % (u'TRUE %s' % order_by) 113 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 114 115 return [ cOrgUnit(row = {'data': r, 'idx': idx, 'pk_field': u'pk_org_unit'}) for r in rows ]
116 117 #====================================================================== 118 # main 119 #---------------------------------------------------------------------- 120 if __name__ == "__main__": 121 122 if len(sys.argv) < 2: 123 sys.exit() 124 125 if sys.argv[1] != u'test': 126 sys.exit() 127 128 129 for unit in get_org_units(): 130 print unit 131 132 sys.exit(0) 133 #============================================================ 134 #============================================================ 135 #============================================================ 136 # old stuff below, disregard 137 #============================================================ 138 #============================================================ 139 140 141 attrNames = [ 'name', 'office', 'subtype', 'memo','category', 'phone', 'fax', 'email', 'mobile' ] 142 addressNames = [ 'number', 'street', 'urb', 'postcode', 'state', 'country'] 143 144 commtypes = { 145 # "email":gmDemographicRecord.EMAIL, 146 # "fax":gmDemographicRecord.FAX, 147 #gmDemographicRecord.HOME_PHONE, 148 # "phone":gmDemographicRecord.WORK_PHONE, 149 # "web":gmDemographicRecord.WEB, 150 # "mobile":gmDemographicRecord.MOBILE, 151 # "jabber":gmDemographicRecord.JABBER 152 } 153 154 commnames = dict( [ (v,k) for (k,v) in commtypes.items()] ) 155 156 workAddressType = 2 # seems constant for gnumed schema in any language 157 158 #addressTypes = gmDemographicRecord.getAddressTypes() 159 160 #class cCatFinder(gmBorg.cBorg):
161 -class cCatFinder:
162
163 - def __init__(self, categoryType = None, pkCol = 'id', nameCol = 'description'):
164 #gmBorg.cBorg.__init__(self) 165 if not self.__dict__.has_key("categories"): 166 self.categories = {} 167 168 if categoryType == None: 169 return 170 171 if not self.categories.has_key(categoryType): 172 self.categories[categoryType] = {'toId': {}, 'toDescription': {},'id': pkCol, 'name': nameCol } 173 self.reload(categoryType)
174 175 176
177 - def reload(self, categoryType):
178 self.__init__(categoryType) 179 pk = self.categories[categoryType]['id'] 180 name = self.categories[categoryType]['name'] 181 result = gmPG.run_ro_query("personalia","select %s, %s from %s" % (pk, name, categoryType)) 182 if result is None: 183 _log.error("failed to load %s" % categoryType) 184 185 for (id, description) in result: 186 self.categories[categoryType]['toId'][description] = id 187 self.categories[categoryType]['toDescription'][id] = description 188 return self.categories[categoryType]
189 190 191
192 - def getId(self, categoryType, category):
193 return self.categories.get(categoryType, self.reload(categoryType)).get('toId',{}).get(category, None)
194
195 - def getCategory(self, categoryType, id):
196 return self.categories.get(categoryType, self.reload(categoryType)).get('toDescription',{}).get(id, "")
197
198 - def getCategories(self, categoryType):
199 return self.categories.get(categoryType, self.reload(categoryType)).get('toId',{}).keys()
200 201 #cCatFinder('org_category') 202 #cCatFinder('enum_comm_types') 203 #cCatFinder('occupation', 'id', 'name') 204 205 DEPARTMENT = 1 206 207 208 209 210 211 #class cOrgHelperImpl1(gmBorg.cBorg, cOrgHelper):
212 -class cOrgHelperImpl1():
213
214 - def __init__(self):
215 # gmBorg.cBorg.__init__(self) 216 # cOrgHelper.__init__(self) 217 self._cache = {} 218 self.setLineSeparatedClipboardFormat()
219 220
222 self._clipboardFormat = "%(name)s,\n%(address_str)s,\nphone:%(phone)s,\nfax:%(fax)s\nemail: %(email)s\nref:%(orgtype)s/%(id)d\n"
223
224 - def setXMLClipboardFormat(self):
225 self._clipboardFormat = """ 226 <%(orgtype)s id='%(id)d'> 227 <name>%(name)s</name> 228 <address>%(address_str)s</address> 229 <phone>%(phone)s</phone> 230 <fax>%(fax)s</fax> 231 <email>%(email)s</email> 232 </(orgtype)s> 233 """
234
235 - def getClipboardText(self, org):
236 237 d = { 'name': org['name'], 238 'address_str': self.getAddressStr(org), 239 'phone' : org['phone'], 240 'fax' : org['fax'], 241 'email' : org['email'], 242 'id': org.getId() 243 } 244 if self.isPerson(org): 245 d['orgtype'] = 'person' 246 247 elif org.getParent() <> None: 248 d['orgtype'] = 'org' 249 d['name'] = ' '.join( [ org['name'], org['subtype'], ',',org.getParent()['name'] ] ) 250 else: 251 d['orgtype'] = 'org' 252 253 # find a non-blank address 254 o = org 255 while o.getParent() <> None and self.getAddressStr(o).strip() == '': 256 d['address_str'] = self.getAddressStr(o.getParent() ) 257 o = o.getParent() 258 259 str = self._clipboardFormat % d 260 261 return str
262
263 - def cacheContains(self,id):
264 return self._cache.has_key(id)
265
266 - def getFromCache(self,id):
267 return self._cache.get(id, None)
268
269 - def updateCache(self,org):
270 self._cache[org.getId()] = org
271
272 - def removeFromCache(self, id):
273 if self._cache.has_key(id): 274 del self._cache[id] 275 return True 276 return False
277
278 - def findAllOrganizations(self):
279 result = gmPG.run_ro_query("personalia", """select id from dem.org""",[]) 280 if result == None: 281 _log.exception("Unable to select id from org") 282 return False 283 284 ids = [ x for [x] in result] 285 return self.findOrgsForIds(ids)
286 287
288 - def findOrgsForIds(self, ids):
289 """ finds org objects by id. returns a list of cOrgHelper objects 290 """ 291 #TODO - caching of org objects on class level. 292 # - listening for backend data changes using pycommon.gmDispatcher 293 # and cache update. 294 return self._findOrgsForIdsCacheCheck(ids)
295 296 297
298 - def _findOrgsForIdsCacheCheck(self, ids):
299 orglist = [] 300 fetch_ids = [] 301 for id in ids: 302 if self.cacheContains(id): 303 org = self.getFromCache(id) 304 orglist.append(org) 305 continue 306 fetch_ids.append(id) 307 308 dbOrgList = self._findOrgsForIdsFromDB( fetch_ids) 309 310 for org in dbOrgList: 311 self.updateCache(org) 312 313 return orglist + dbOrgList
314
315 - def _findOrgsForIdsFromDB(self, fetch_ids):
316 if fetch_ids == None or fetch_ids == []: 317 return [] 318 319 om = get_org_data_for_org_ids(fetch_ids) 320 cm = get_comm_channels_data_for_org_ids(fetch_ids ) 321 am = get_address_data_for_org_ids( fetch_ids) 322 m = {} 323 orglist = [] 324 for id in fetch_ids: 325 org = self.create() 326 if not org._load_org_from_tuple(om.get(id, None), id): 327 continue 328 org._load_comm_channels_from_tuples( cm.get(id, None) ) 329 org._load_address_from_tuple( am.get(id, None) ) 330 orglist.append(org) 331 return orglist
332
333 - def findOrgsByName( self, name, exact = False):
334 """the org name is a unique key, so should only return one or none org""" 335 if exact: query= "select id from dem.org where description = '%s'"%name 336 else: query = "select id from dem.org where description like '%s%%'"%name 337 338 result = gmPG.run_ro_query("personalia", query ) 339 if result is None: 340 _log.error("Unable to find org by name %s" % name) 341 return [None] 342 343 return self.findOrgsForIds([ x[0] for x in result])
344 345
346 - def findAllOrganizationPKAndName(self):
347 return [ (0,"") ]
348 349
350 - def create(self):
351 return cOrgImpl1()
352 353
354 - def getAddressStr(self, org):
355 a = org.getAddress() 356 return " ".join( [a.get('number','').strip(), a.get('street','').strip(), a.get('urb','').strip(), a.get('postcode','')])
357 358 359
360 -class cOrgHelperImpl2(cOrgHelperImpl1):
361
362 - def __init__(self):
364
365 - def create(self):
366 return cCompositeOrgImpl1()
367
368 - def findOrgsForIds(self, ids):
369 """extends cOrgHelperImpl1's findOrgsforIds and orders them 370 parent/ child order""" 371 372 l = cOrgHelperImpl1.findOrgsForIds(self, ids) 373 childMap = {} 374 parents = filter( lambda(o): o.getParent() is None, l) 375 childMap = dict( [ (p.getId() , []) for p in parents ] ) 376 for o in l: 377 if o in parents: 378 continue 379 childMap[o.getParent().getId()].append(o) 380 381 l2 = [] 382 for p in parents: 383 l2.append(p) 384 for c in childMap[p.getId()]: 385 l2.append(c) 386 387 return l2
388 389
390 -class _cPersonMarker:
391 """marker class, for person type check""" 392 pass
393
394 -class cOrgHelperImpl3(cOrgHelperImpl2):
395 """extends org/suborg handling of cOrgHelperImpl2 to handle org persons""" 396
397 - def __init__(self):
399
400 - def create(self):
401 return cOrgHelperImpl2.create(self)
402
403 - def createOrgPerson(self):
404 return cOrgDemographicAdapter()
405
406 - def isPersonOrg(self, org):
407 return _cPersonMarker in inspect.getmro(org.__class__)
408
409 - def isPerson(self, org):
410 return self.isPersonOrg(org)
411
412 -class cOrgImpl1(cOrg):
413 414 _cache = {} 415 #----------------------------------------- 416 # instance methods 417 #-------------------------------------------------------------------------- 418
419 - def __init__(self, helper = cOrgHelperImpl1() ):
420 self._map = dict ( [ (n,'') for n in attrNames] ) 421 422 self._changed= {} 423 self.pk = None 424 self._addressModified(False) 425 self._address = {} 426 427 self._personMap = {} 428 self._helper = helper 429 pass
430
431 - def getHelper(self):
432 return self._helper
433
434 - def getId(self):
435 return self.pk
436
437 - def setId(self, pk):
438 self.pk = pk
439
440 - def getAddress(self):
441 return self.getAddressDict()
442
443 - def getAddressDict(self):
444 d = {} 445 d.update(self._address) 446 return d
447
448 - def setAddress(self, number, street, urb, postcode, state, country):
449 self._setAddressImpl( number, street, urb, postcode, state, country)
450
451 - def _setAddressImpl( self, *kwrd, **kargs):
452 names = addressNames 453 if kargs == {} and kwrd <> []: 454 kargs = dict( [ (a, v) for a,v in zip( names, kwrd) ] ) 455 456 457 for k in names: 458 a = self._address 459 if a.get(k, None) <> kargs.get(k, None): 460 self._addressModified(True) 461 a[k] = kargs[k]
462 463
464 - def _addressModified(self, val = None):
465 if val <> None: 466 self._amodified = val 467 return self._amodified
468
469 - def set(self, name, office, subtype, memo, category, phone, fax, email,mobile):
470 self._set_impl(name, office, subtype, memo, category, phone, fax, email,mobile)
471 472
473 - def _set_impl(self, *kwrd, **kargs):
474 """ 475 476 """ 477 n = attrNames 478 if kargs == {} and kwrd <> []: 479 kargs = dict( [ (a, v) for a,v in zip( n, kwrd) ] ) 480 481 changed = {} 482 for k in n: 483 v = self._map.get(k, None) 484 485 if v != kargs[k]: 486 changed[k] = kargs[k] 487 488 self._changed = changed
489
490 - def __setitem__(self, k, v):
491 if k in attrNames and self._map.get(k, None) <> v: 492 self._changed[k] = v
493
494 - def __getitem__(self, k):
495 v = self._changed.get(k, None) 496 if v == None: 497 v = self._map.get(k, None) 498 499 return v
500 501
502 - def _save_comm_channels(self):
503 if self.getId() is None: 504 _log.error("Unable to save comm channel %s : %s due to no org id" % (k,v) ) 505 return False 506 507 comm_changes = {} 508 for k,id_type in commtypes. items(): 509 if self._changed.has_key(k): 510 comm_changes[id_type] = self._changed[k] 511 512 urls = comm_changes.values() 513 if urls == []: 514 return True 515 516 places = ['%s'] *len(urls) 517 518 format = ', '.join(places) 519 520 cmd = [ 521 ("""select id, url, id_type from dem.comm_channel where url in( %s )""" % format, urls) ] 522 result = gmPG.run_commit('personalia', cmd) 523 if result is None: 524 _log.error("find existing org comms failed" ) 525 return False 526 527 528 existing_urls = dict( [ (url,(id, id_type) ) for (id, url, id_type) in result] ) 529 for id_type , url in comm_changes.items(): 530 if url in existing_urls.keys() and existing_urls[url][1] <> id_type: 531 _log.warning("Existing comm url mismatches type for org url %s, inserting same url different type!" % url) 532 del existing_urls[url] 533 cmds = [] 534 535 delete_link_cmd = """delete from dem.lnk_org2comm_channel 536 where id_comm in ( 537 select l2.id_comm from 538 dem.lnk_org2comm_channel l2 , dem.comm_channel c 539 where c.id = l2.id_comm 540 and c.id_type = %d 541 and l2.id_org = %d 542 ) """ 543 544 for url in existing_urls.keys(): 545 (id_comm, id_type) = existing_urls[url] 546 cmds = [ (delete_link_cmd % (id_type, self.getId()) ,[] ), 547 ("""insert into dem.lnk_org2comm_channel( id_comm, id_org) 548 values ( %d, %d ) """ % ( id_comm, self.getId() ) , [] ) 549 ] 550 551 for id_type, url in comm_changes.items(): 552 if url in existing_urls.keys(): 553 continue 554 555 if url.strip() == "": 556 cmds.append( 557 (delete_link_cmd %(id_type, self.getId()) , [] ) 558 ) 559 else: 560 561 cmds.append( 562 ("""insert into dem.comm_channel( url, id_type) 563 values( '%s', %d)""" % (url, id_type),[] ) 564 ) 565 cmds.append( 566 ("""insert into dem.lnk_org2comm_channel(id_comm, id_org) 567 values( currval('comm_channel_id_seq'), %d)""" % 568 self.getId() ,[] ) ) 569 570 571 result = gmPG.run_commit('personalia',cmds)
572
573 - def _save_address(self):
574 a = self._address 575 576 if not self._addressModified(): 577 return True 578 579 # allow for no address 580 if a['urb'].strip() == '': 581 return True 582 583 return self.linkNewAddress(a['number'],a['street'], a['urb'], a['postcode'], a.get('state', None), a.get('country', None) )
584 585 586
587 - def linkNewAddress (self, number, street, urb, postcode, state = None, country = None):
588 """Adds a new address into this org list of addresses. Basically cut and 589 paste and delete unnecessary fields from gmDemographics function. 590 """ 591 urb = urb.upper() 592 if state == "": state = None 593 if country == "": country = None 594 595 596 597 if state is None: 598 print "urb, postcode", urb, postcode 599 state, country = gmDemographicRecord.guess_state_country(urb, postcode) 600 print "state, country", state, country 601 # address already in database ? 602 cmd = """ 603 select addr_id from dem.v_basic_address 604 where 605 number = %s and 606 street = %s and 607 city = %s and 608 postcode = %s and 609 state = %s and 610 country = %s 611 """ 612 data = gmPG.run_ro_query ('personalia', cmd, None, number, street, urb, postcode, state, country) 613 if data is None: 614 s = " ".join( ( number, street, urb, postcode, state, country ) ) 615 _log.error('cannot check for address existence (%s)' % s) 616 return None 617 618 # delete any pre-existing link for this org 619 cmd = """ 620 delete from dem.lnk_person_org_address 621 where 622 id_org = %s 623 """ 624 gmPG.run_commit ('personalia', [(cmd, [self.getId()])]) 625 626 # yes, address already there, just add the link 627 if len(data) > 0: 628 addr_id = data[0][0] 629 cmd = """ 630 insert into dem.lnk_person_org_address (id_org, id_address) 631 values (%d, %d) 632 """ % (self.getId(), addr_id) 633 return gmPG.run_commit ("personalia", [ ( cmd,[]) ]) 634 635 # no, insert new address and link it, too 636 cmd1 = """ 637 insert into dem.v_basic_address (number, street, city, postcode, state, country) 638 values (%s, %s, %s, %s, %s, %s) 639 """ 640 cmd2 = """ 641 insert into dem.lnk_person_org_address (id_org, id_address) 642 values (%d, currval('address_id_seq')) 643 """ % self.getId() 644 return gmPG.run_commit ("personalia", [ 645 (cmd1, (number, street, urb, postcode, state, country)), 646 (cmd2, [] ) 647 ] 648 )
649 650 651
652 - def get(self):
653 m = {} 654 m.update(self._map) 655 m.update(self._changed) 656 return m 657 658
659 - def load(self, pk):
660 return ( self._load_org(pk) and 661 self._load_comm_channels() 662 and self._load_address() )
663 664 665 666
667 - def _load_org(self, pk):
668 m_org = get_org_data_for_org_ids( [pk] ) 669 if m_org == None or not m_org.has_key(pk): 670 #<DEBUG> 671 print "org id = ", pk, " not found" 672 #</DEBUG> 673 return False 674 self._load_org_from_tuple(m_org[pk], pk) 675 676
677 - def _load_org_from_tuple(self, tuple, pk = None):
678 if tuple == None or tuple == []: 679 self.setId(None) 680 return False 681 682 (description, id_category) = tuple 683 m=self._map 684 cf = cCatFinder() 685 m['category']=cf.getCategory("org_category",id_category) 686 687 m['name']=description 688 self.setId(pk) 689 690 return True
691 692
693 - def _load_comm_channels(self):
694 """uses get_comm_channels_data_for_org_ids with only a singleton id list, 695 with the current id to be fetched, then convert to self._map so 696 can be read from self.get() #returning a map of comm channel types vs urls""" 697 m = get_comm_channels_data_for_org_ids([ self.getId() ] ) 698 if m == None: 699 return False 700 701 if m.has_key(self.getId()): 702 return self._load_comm_channels_from_tuples(m[self.getId()])
703 704 705
706 - def _load_comm_channels_from_tuples(self, rows):
707 if rows == None : 708 return False 709 n = commnames 710 for ( id_type, url) in rows: 711 if commnames.has_key(int(id_type)): 712 self._map[commnames[id_type]] = url 713 714 return True
715
716 - def _load_address(self):
717 m = get_address_data_for_org_ids( [self.getId()]) 718 if m == None: 719 return False 720 721 if not m.has_key(self.getId() ): 722 _log.error("No address for org" ) 723 return True 724 725 return self._load_address_from_tuple( m[self.getId()] )
726 727
728 - def _load_address_from_tuple(self, r):
729 #precondition: must be a tuple and have right number of fields 730 if r == None or len(r) < 6: 731 return False 732 733 self._address = { 'number':r[0], 'street':r[1], 'urb':r[2], 'postcode':r[3], 'state':r[4], 'country': r[5] } 734 735 self._addressModified(False) 736 737 return True 738 739
740 - def shallow_del(self):
741 cmds = [ 742 ("delete from dem.lnk_person_org_address where id_org = %d"%self.getId() , [] ), 743 ("delete from dem.lnk_org2comm_channel where id_org = %d"%self.getId(),[] ), 744 ("delete from dem.org where id = %d"%self.getId() , [] ) 745 ] 746 747 if (gmPG.run_commit('personalia',cmds) == None): 748 _log.error("failed to remove org") 749 return False 750 751 self.setId(None) 752 753 return True
754 755 756 757
758 - def _create(self):
759 #<DEBUG> 760 #print "in _create" 761 #</DEBUG> 762 v = self['name'] 763 if v <> None: 764 cmd = "select id from dem.org where description = '%s'" % v 765 result = gmPG.run_ro_query('personalia', cmd) 766 if result <> None and len(result) <> 0: 767 self.setId(result[0][0]) 768 return True 769 770 771 cmd = ("""insert into dem.org (description, id_category) values('xxxDefaultxxx', ( select id from dem.org_category limit 1) )""", []) 772 cmd2 = ("""select currval('dem.org_id_seq')""", []) 773 result = gmPG.run_commit('personalia', [cmd, cmd2]) 774 if result is None: 775 cmd = ("""select id from dem.org where description ='xxxDefaultxxx'""",[]) 776 result = gmPG.run_commit('personalia', [cmd] ) 777 if result <> None and len(result) == 1: 778 self.setId(result[0][0]) 779 #<DEBUG> 780 #print "select id from org ->", self.getId() 781 #</DEBUG> 782 return True 783 return False 784 self.setId(result[0][0]) 785 #<DEBUG> 786 #print "from select currval -> ", self.getId() 787 #</DEBUG> 788 return True
789
790 - def save(self):
791 792 #TODO only the name, category attributes are saved; sql places for memo , office, subtype needed. 793 m={} 794 c = self._changed 795 m.update(self._map) 796 m.update(self._map) 797 m.update(c) 798 if not m.has_key('name') or m['name'].strip() =='': 799 print "PLEASE ENTER ORG NAME" #change this 800 return False 801 print "self.getId() = ", self.getId() , " is None : ", self.getId() is None 802 if self.getId() is None: 803 if not self._create(): 804 import sys 805 _log.error("Cannot create org") 806 return False 807 if self.getId() is None: 808 return False 809 # c is any changed, m is what is current 810 if c.has_key('name') or c.has_key('category'): 811 812 #print "pk = ", self.getId() 813 #org = cOrganization(str(self.getId())) 814 cf = cCatFinder() 815 #print "cCatFinder", cf.getCategories('org_category') 816 #print "m['category']", m['category'], "cf.getId(.. = ", cf.getId('org_category', m['category']) 817 cmd = """ 818 update dem.org set description='%s' , 819 id_category = %s where id = %s 820 """ % ( m['name'], 821 str( cf.getId('org_category', m['category']) ), 822 str(self.getId()) ) 823 result = gmPG.run_commit( "personalia", [ (cmd,[] ) ] ) 824 if result is None: 825 _log.error("Cannot save org") 826 return False 827 828 self._save_address() 829 self._save_comm_channels() 830 self._helper.updateCache(self) 831 return True
832
833 - def linkPerson( self, demRecord): # demRecord is a cDemographicRecord
834 if self.getId() == None: 835 return False, _( "Org must be saved before adding persons") 836 837 # not needed currently, but just in case 838 if demRecord.getID() is None: 839 return False, _("demRecord doesn't have an ID ! Impossible !") 840 841 self._personMap[int(demRecord.getID())] = demRecord 842 843 # checked already linked 844 cmd = "select id from dem.lnk_person_org_address where id_identity = %d and id_org = %d" % (int(demRecord.getID()), self.getId() ) 845 846 result = gmPG.run_ro_query("personalia", cmd,[]) 847 if not result is None and len(result) == 1: 848 return True, _("Ok") 849 850 cmd = "insert into dem.lnk_person_org_address(id_identity, id_org) values (%d,%d)" % ( int(demRecord.getID()), self.getId() ) 851 852 result = gmPG.run_commit("personalia", [ (cmd,[]) ] ) 853 854 if result is None: 855 _log.error("Cannot link person") 856 return False, _("SQL failed for link persons") 857 858 return True, _("Ok") 859
860 - def unlinkPerson(self, demographicRecord):
861 if self.getId() == None: 862 return False, _("Org must be saved before adding persons") 863 864 cmd = """delete from dem.lnk_person_org_address where id_identity = %d 865 and id_org = %d """ % ( int(demographicRecord.getID()) , self.getId() ) 866 867 result = gmPG.run_commit("personalia", [ (cmd,[]) ] ) 868 869 if result is None: 870 _log.error("Cannot unlink person") 871 return False 872 873 del self._personMap[demographicRecord.getID()] # unlink in cache as well 874 875 return True
876 877
878 - def getPersonMap(self, reload = True):
879 """gets the persons associated with this org, lazy loading demographic records 880 and caching if needed; need to later use a singleton demographic cache, 881 so that single copies of a demographic record is shared """ 882 if self.getId() == None: 883 return {} 884 885 m = {} 886 m.update(self._personMap) 887 888 if not reload and not self._personMap == {} : 889 return m 890 891 query = "select id_identity from dem.lnk_person_org_address where id_org = %d"% self.getId() 892 result = gmPG.run_ro_query("personalia", query) 893 print "for ", query, " got ", result 894 if result is None: 895 _log.error("Cannot search for org persons") 896 return None 897 898 ids = filter( lambda(t): t <> None, [ id for [id] in result ]) 899 print "id list is ", ids 900 new_ids = filter( lambda(id): id not in m.keys(), ids) 901 902 for id in new_ids: 903 rec = gmDemographicRecord.cDemographicRecord_SQL(id) 904 m[id] = rec 905 906 self._personMap.update(m) 907 908 return m
909 910
911 -class cCompositeOrgImpl1( cOrgImpl1):
912 """this class behaves differently from cOrgImpl1 iff there is a parent org""" 913
914 - def __init__(self, parent = None, helper = cOrgHelperImpl2() ):
915 cOrgImpl1.__init__(self, helper) 916 self._parent = parent
917
918 - def _create(self):
919 920 if not cOrgImpl1._create(self): 921 922 return False 923 924 return self._saveCompositeName()
925
926 - def save(self):
927 """if getParent() is None, then the behaviour is 928 unchanged from cOrgImpl1, but if there is a parent org, 929 then there will also sub-org information saved in the description""" 930 931 if not cOrgImpl1.save(self): 932 return False 933 return self._saveCompositeName()
934 935 936
937 - def _saveCompositeName(self):
938 parent = self.getParent() 939 if parent == None: 940 return True 941 942 new_description = '\n'.join([parent['name'] , self['name'], self['subtype']]) 943 result = gmPG.run_commit("personalia", [ ("""update dem.org set description='%s' where id=%d 944 """ % (new_description, self.getId() ), [] ) ]) 945 if result == None: 946 _log.exception("unable to update sub-org name") 947 return False 948 return True
949 950
951 - def _load_org_from_tuple(self, tuple, pk = None):
952 """this loads the org like cOrgImpl1, but then checks for 953 additional sub-org information in the 'name' aka org.description, 954 and if it exists, the parent is retrieved or constructed using 955 the findOrgByName function. 956 """ 957 958 if not cOrgImpl1._load_org_from_tuple(self, tuple, pk): 959 return False 960 961 # first extended behaviour, recognise subtype attribute. 962 self['subtype'] = '' 963 964 l = self['name'].split('\n') 965 print "split org name into ", l 966 if len(l) < 3: 967 return True 968 969 (parentName, self['name'], self['subtype'] ) = l 970 orgList = self._helper.findOrgsByName(parentName, exact = True) 971 if orgList == []: 972 return True 973 org = orgList[0] 974 self.setParent(org) 975 976 return True
977 978
979 - def getParent(self):
980 return self._parent
981
982 - def setParent(self, parent):
983 self._parent = parent
984 985 986 987
988 -class cOrgDemographicAdapter(cOrg, _cPersonMarker):
989
990 - def __init__(self, parent = None, helper = cOrgHelperImpl3()):
991 self._parent = parent 992 self._helper = helper 993 self._record = None 994 self._data = { 'name':'', 995 'subtype':'', 996 'memo':'', 997 'phone':'', 998 'fax':'', 999 'email':'', 1000 'mobile': '' 1001 } 1002 1003 self._address = { 1004 'number':'', 1005 'street':'', 1006 'urb' :'', 1007 'postcode': '', 1008 'state' : None, 1009 'country': None 1010 }
1011
1012 - def getHelper(self):
1013 return self._helper
1014
1015 - def setDemographicRecord(self, record):
1016 self._record = record 1017 self._parseRecord()
1018
1019 - def getDemographicRecord(self):
1020 return self._record
1021
1022 - def getId(self):
1023 if self._record is None: 1024 return None 1025 return self._record.getID()
1026 1027
1028 - def setId(self, id): # ? change to non-public
1029 pass
1030
1031 - def set(self, name, office, subtype, memo, category, phone, fax, email,mobile = ""):
1032 d = self._data 1033 s = { 'name':name, 1034 'office': office, 1035 'subtype': subtype, 1036 'memo': memo, 1037 'category': category, 1038 'phone': phone, 1039 'fax' : fax, 1040 'email' : email, 1041 'mobile': mobile 1042 } 1043 for k in d.keys(): 1044 d[k] = s[k]
1045 #<DEBUG> 1046 #print 'data ', k, ' has been set to ', d[k] 1047 #</DEBUG> 1048 1049 1050 1051
1052 - def setAddress(self, number, street, urb, postcode, state, country):
1053 d = self._address 1054 s = { 'number': number, 1055 'street': street, 1056 'urb' : urb, 1057 'postcode' : postcode, 1058 'state' : state, 1059 'country': country 1060 } 1061 1062 1063 for k in s.keys(): 1064 d[k] = s[k]
1065 #<DEBUG> 1066 #print "self._address is now ", self._address 1067 #</DEBUG> 1068
1069 - def getAddress(self):
1070 m = {} 1071 m.update(self._address) 1072 return m
1073 1074 1075
1076 - def __setitem__(self, k, v):
1077 d = self._data 1078 if d.has_key(k): 1079 d[k] = v 1080 return True 1081 return False
1082
1083 - def __getitem__(self, k):
1084 d = self._data 1085 if d.has_key(k): 1086 return d[k] 1087 return None
1088 1089
1090 - def get(self):
1091 m = {} 1092 m.update(self._data) 1093 return m 1094
1095 - def load(self, pk):
1096 self.setDemographicRecord(gmDemographicRecord.cDemographicRecord_SQL(pk))
1097
1098 - def _parseRecord(self):
1099 d = self._data 1100 r = self._record 1101 n = r.get_names() 1102 if n['title'][-1] <> '.': 1103 n['title'] = n['title'] + '.' 1104 d['name'] = ' '.join([n['title'], n['firstnames'], n['lastnames'] ]) 1105 if r.getOccupation() : 1106 d['subtype'] = r.getOccupation() 1107 1108 for k,id in commtypes.items(): 1109 v = r.getCommChannel(id) 1110 if v: d[k] = v 1111 1112 addressTypes = gmDemographicRecord.getAddressTypes() 1113 address = r.getAddresses( addressTypes[workAddressType], firstonly=1) 1114 a = self._address 1115 #<DEBUG> 1116 print "got back address from demographic record", address 1117 #</DEBUG> 1118 if address is None: 1119 return 1120 1121 fields = ['number', 'street', 'urb', 'postcode'] 1122 if type(address) is type([]) and len(address) >0: 1123 if type(address[0]) is type({}): 1124 address = address[0] 1125 elif type(address[0]) is type(''): 1126 a = dict ( [(k,v) for k,v in zip( fields, address) ] ) 1127 return 1128 1129 for k in fields: 1130 if type(address) is type({}): 1131 a[k] = address.get(k, '')
1132 1133
1134 - def save(self):
1135 print "Called save on orgPersonAdapter" 1136 if self.getParent() is None: 1137 print "no parent" 1138 _log.error("This orgPersonAdapter needs a parent org") 1139 return False 1140 1141 if self.getId() is None: 1142 print "no id" 1143 if not self._create(): 1144 print "can't create an id" 1145 return False 1146 1147 1148 r = self._record 1149 d = self._data 1150 1151 print "splitting name" 1152 1153 l0 = d['name'].split('.') 1154 if len(l0) > 1: 1155 if len(l0) > 2: 1156 print "ambiguous title separation at '.'" 1157 title = l0[0] + '.' 1158 name = " ".join( l0[1:]) 1159 else: 1160 name = d['name'] 1161 title = '' 1162 1163 l1 = name.split(',') 1164 1165 # parse the name field 1166 if len(l1) == 2: 1167 # assume "lastnames , firstnames" format 1168 l = [ x.strip() for x in l1] 1169 first , last = l[1], l[0] 1170 else: 1171 l1 = name.split(' ') 1172 l = [ x.strip() for x in l1] 1173 # try the UPPER CASE IS LASTNAME starting from last word 1174 inUpper = -1 1175 while inUpper > -len(l) and l[inUpper - 1].isupper(): 1176 inUpper -= 1 1177 1178 first, last = ' '.join(l[0:inUpper]), ' '.join(l[inUpper:]) 1179 print "adding name" 1180 r.addName(first, last, True) 1181 r.setTitle(title) 1182 1183 if r.setOccupation( d['subtype']) is None: 1184 print "FAILED TO save occupation" 1185 print "record occupation is ", r.getOccupation() 1186 1187 for k in commtypes.keys(): 1188 v = d.get(k,'') 1189 if v is None or v.strip() == '': 1190 continue 1191 t = commtypes[k] 1192 r.linkCommChannel( t, v) 1193 1194 1195 a = self._address 1196 1197 if a['urb'].strip() <> '' and a['street'].strip() <> '': 1198 r.linkNewAddress( addressTypes[workAddressType], 1199 a['number'], 1200 a['street'], 1201 a['urb'], 1202 a['postcode'] ) 1203 1204 self.getParent().linkPerson(self.getDemographicRecord()) 1205 return True
1206 1207
1208 - def _create(self):
1209 id = gmPerson.create_dummy_identity() 1210 if id is None: 1211 return False 1212 self._record = gmDemographicRecord.cDemographicRecord_SQL(id) 1213 return True
1214
1215 - def getParent(self):
1216 return self._parent
1217
1218 - def setParent(self, parent):
1219 self._parent = parent
1220 1221 1222 1223
1224 -def get_comm_channels_data_for_org_ids( idList):
1225 """gets comm_channels for a list of org_id. 1226 returns a map keyed by org_id with lists of comm_channel data (url, type). 1227 this allows a single fetch of comm_channel data for multiple orgs""" 1228 1229 ids = ", ".join( [ str(x) for x in idList]) 1230 cmd = """select l.id_org, id_type, url 1231 from dem.comm_channel c, dem.lnk_org2comm_channel l 1232 where 1233 c.id = l.id_comm and 1234 l.id_org in ( select id from dem.org where id in (%s) ) 1235 """ % ids 1236 result = gmPG.run_ro_query("personalia", cmd) 1237 if result == None: 1238 _log.error("Unable to load comm channels for org" ) 1239 return None 1240 m = {} 1241 for (id_org, id_type, url) in result: 1242 if not m.has_key(id_org): 1243 m[id_org] = [] 1244 m[id_org].append( (id_type, url) ) 1245 1246 return m # is a Map[id_org] = list of comm_channel data 1247
1248 -def get_address_data_for_org_ids( idList):
1249 """gets addresses for a list of valid id values for orgs. 1250 returns a map keyed by org_id with the address data 1251 """ 1252 1253 ids = ", ".join( [ str(x) for x in idList]) 1254 cmd = """select l.id_org, number, street, city, postcode, state, country 1255 from dem.v_basic_address v , dem.lnk_org2address l 1256 where v.addr_id = l.id_address and 1257 l.id_org in ( select id from dem.org where id in (%s) ) """ % ids 1258 result = gmPG.run_ro_query( "personalia", cmd) 1259 1260 if result == None: 1261 _log.error("failure in org address load" ) 1262 return None 1263 m = {} 1264 for (id_org, n,s,ci,p,st,co) in result: 1265 m[id_org] = (n,s,ci,p,st,co) 1266 return m
1267
1268 -def get_org_data_for_org_ids(idList):
1269 """ for a given list of org id values , 1270 returns a map of id_org vs. org attributes: description, id_category""" 1271 1272 ids = ", ".join( [ str(x) for x in idList]) 1273 cmd = """select id, description, id_category from dem.org 1274 where id in ( select id from dem.org where id in( %s) )""" % ids 1275 #<DEBUG> 1276 print cmd 1277 #</DEBUG> 1278 result = gmPG.run_ro_query("personalia", cmd, ) 1279 if result is None: 1280 _log.error("Unable to load orgs with ids (%s)" %ids) 1281 return None 1282 m = {} 1283 for (id_org, d, id_cat) in result: 1284 m[id_org] = (d, id_cat) 1285 return m
1286 1287 1288 1289 1290 #============================================================ 1291 # 1292 # IGNORE THE FOLLOWING, IF NOT INTERESTED IN TEST CODE 1293 # 1294 # 1295 1296 if __name__ == '__main__': 1297 print "Please enter a write-enabled user e.g. _test-doc " 1298
1299 - def testListOrgs():
1300 print "running test listOrg" 1301 for (f,a) in get_test_data(): 1302 h = cOrgImpl1() 1303 h.set(*f) 1304 h.setAddress(*a) 1305 if not h.save(): 1306 print "did not save ", f 1307 1308 orgs = cOrgHelperImpl1().findAllOrganizations() 1309 1310 for org in orgs: 1311 print "Found org ", org.get(), org.getAddress() 1312 if not org.shallow_del(): 1313 print "Unable to delete above org"
1314 1315 1316 1317 1318 1319
1320 - def get_test_data():
1321 """test org data for unit testing in testOrg()""" 1322 return [ 1323 ( ["Box Hill Hospital", "", "", "Eastern", "hospital", "0398953333", "111-1111","bhh@oz", ""], ["33", "Nelson Rd", "Box Hill", "3128", None , None] ), 1324 ( ["Frankston Hospital", "", "", "Peninsula", "hospital", "0397847777", "03784-3111","fh@oz", ""], ["21", "Hastings Rd", "Frankston", "3199", None , None] ) 1325 ]
1326
1327 - def get_test_persons():
1328 return { "Box Hill Hospital": 1329 [ 1330 ['Dr.', 'Bill' , 'Smith', '123-4567', '0417 111 222'], 1331 ['Ms.', 'Anita', 'Jones', '124-5544', '0413 222 444'], 1332 ['Dr.', 'Will', 'Stryker', '999-4444', '0402 333 111'] ], 1333 "Frankston Hospital": 1334 [ [ "Dr.", "Jason", "Boathead", "444-5555", "0403 444 2222" ], 1335 [ "Mr.", "Barnie", "Commuter", "222-1111", "0444 999 3333"], 1336 [ "Ms.", "Morita", "Traveller", "999-1111", "0222 333 1111"]] }
1337
1338 - def testOrgPersons():
1339 m = get_test_persons() 1340 d = dict( [ (f[0] , (f, a)) for (f, a) in get_test_data() ] ) 1341 for orgName , personList in m.items(): 1342 f1 , a1 = d[orgName][0], d[orgName][1] 1343 _testOrgClassPersonRun( f1, a1, personList, cOrgImpl1) 1344 _testOrgClassPersonRun( f1, a1, personList, cCompositeOrgImpl1) 1345 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl1().create) 1346 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl2().create) 1347 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl3().create) 1348 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl3().create, getTestIdentityUsing_cOrgDemographicAdapter)
1349 1350 1351
1352 - def _outputPersons( org):
1353 m = org.getPersonMap() 1354 1355 if m== []: 1356 print "NO persons were found unfortunately" 1357 1358 print """ TestOrgPersonRun got back for """ 1359 a = org.getAddress() 1360 print org["name"], a["number"], a["street"], a["urb"], a["postcode"] , " phone=", org['phone'] 1361 1362 for id, r in m.items(): 1363 print "\t",", ".join( [ " ".join(r.get_names().values()), 1364 "work no=", r.getCommChannel(gmDemographicRecord.WORK_PHONE), 1365 "mobile no=", r.getCommChannel(gmDemographicRecord.MOBILE) 1366 ] )
1367 1368
1369 - def _testOrgPersonRun(f1, a1, personList):
1370 print "Using test data :f1 = ", f1, "and a1 = ", a1 , " and lp = ", personList 1371 print "-" * 50 1372 _testOrgClassPersonRun( f1, a1, personList, cOrgImpl1) 1373 _testOrgClassPersonRun( f1, a1, personList, cCompositeOrgImpl1) 1374 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl1().create) 1375 _testOrgClassPersonRun( f1, a1, personList, cOrgHelperImpl2().create)
1376 1377
1378 - def _setIdentityTestData(identity, data):
1379 identity.addName(data[1], data[2], True) 1380 identity.setTitle(data[0]) 1381 identity.linkCommChannel( gmDemographicRecord.WORK_PHONE, data[3]) 1382 identity.linkCommChannel( gmDemographicRecord.MOBILE, data[4])
1383
1384 - def getTestIdentityUsingDirectDemographicRecord( data, org):
1385 id = gmPerson.create_dummy_identity() 1386 identity = gmDemographicRecord.cDemographicRecord_SQL(id) 1387 _setIdentityTestData(identity, data) 1388 return identity
1389
1390 - def getTestIdentityUsing_cOrgDemographicAdapter( data, org):
1391 helper = cOrgHelperImpl3() 1392 orgPerson= helper.createOrgPerson() 1393 orgPerson.setParent(org) 1394 orgPerson['name'] = ' '.join( [data[0], data[1], data[2]]) 1395 orgPerson['phone'] = data[3] 1396 orgPerson['mobile'] = data[4] 1397 orgPerson.save() 1398 return orgPerson.getDemographicRecord()
1399 1400
1401 - def _testOrgClassPersonRun(f1, a1, personList, orgCreate, identityCreator = getTestIdentityUsingDirectDemographicRecord):
1402 print "-" * 50 1403 print "Testing org creator ", orgCreate 1404 print " and identity creator ", identityCreator 1405 print "-" * 50 1406 h = orgCreate() 1407 h.set(*f1) 1408 h.setAddress(*a1) 1409 if not h.save(): 1410 print "Unable to save org for person test" 1411 h.shallow_del() 1412 return False 1413 # use gmDemographicRecord to convert person list 1414 for lp in personList: 1415 identity = identityCreator(lp, h) 1416 result , msg = h.linkPerson(identity) 1417 print msg 1418 1419 _outputPersons(h) 1420 deletePersons(h) 1421 1422 if h.shallow_del(): 1423 print "Managed to dispose of org" 1424 else: 1425 print "unable to dispose of org" 1426 1427 return True
1428 1429 # def testOrgPerson(f1, a1, personList): 1430
1431 - def deletePerson(id):
1432 cmds = [ ( "delete from dem.lnk_identity2comm_chan where fk_identity=%d"%id,[]), 1433 ("delete from dem.names where id_identity=%d"%id,[]), 1434 ("delete from dem.identity where id = %d"%id,[]) ] 1435 result = gmPG.run_commit("personalia", cmds) 1436 return result
1437
1438 - def deletePersons( org):
1439 map = org.getPersonMap() 1440 for id, r in map.items(): 1441 org.unlinkPerson(r) 1442 1443 result = deletePerson(r.getID()) 1444 if result == None: 1445 _log.error("FAILED TO CLEANUP PERSON %d" %r.getID() )
1446 1447 1448
1449 - def testOrg():
1450 """runs a test of load, save , shallow_del on items in from get_test_data""" 1451 l = get_test_data() 1452 results = [] 1453 for (f, a) in l: 1454 result, obj = _testOrgRun(f, a) 1455 results.append( (result, obj) ) 1456 return results
1457 1458 1459
1460 - def _testOrgRun( f1, a1):
1461 1462 print """testing single level orgs""" 1463 f = [ "name", "office", "subtype", "memo", "category", "phone", "fax", "email","mobile"] 1464 a = ["number", "street", "urb", "postcode", "state", "country"] 1465 h = cOrgImpl1() 1466 1467 h.set(*f1) 1468 h.setAddress(*a1) 1469 1470 print "testing get, getAddress" 1471 print h.get() 1472 print h.getAddressDict() 1473 1474 import sys 1475 if not h.save(): 1476 print "failed to save first time. Is an old test org needing manual removal?" 1477 return False, h 1478 print "saved pk =", h.getId() 1479 1480 1481 pk = h.getId() 1482 if h.shallow_del(): 1483 print "shallow deleted ", h['name'] 1484 else: 1485 print "failed shallow delete of ", h['name'] 1486 1487 1488 1489 h2 = cOrgImpl1() 1490 1491 print "testing load" 1492 1493 print "should fail" 1494 if not h2.load(pk): 1495 print "Failed as expected" 1496 1497 if h.save(): 1498 print "saved ", h['name'] , "again" 1499 else: 1500 print "failed re-save" 1501 return False, h 1502 1503 h['fax'] = '222-1111' 1504 print "using update save" 1505 1506 if h.save(): 1507 print "saved updated passed" 1508 print "Test reload next" 1509 else: 1510 print "failed save of updated data" 1511 print "continuing to reload" 1512 1513 1514 if not h2.load(h.getId()): 1515 print "failed load" 1516 return False, h 1517 print "reloaded values" 1518 print h2.get() 1519 print h2.getAddressDict() 1520 1521 print "** End of Test org" 1522 1523 if h2.shallow_del(): 1524 print "cleaned up" 1525 else: 1526 print "Test org needs to be manually removed" 1527 1528 return True, h2
1529
1530 - def clean_test_org():
1531 l = get_test_data() 1532 1533 names = [ "".join( ["'" ,str(org[0]), "'"] ) for ( org, address) in l] 1534 names += [ "'John Hunter Hospital'", "'Belmont District Hospital'"] 1535 nameList = ",".join(names) 1536 categoryList = "'hospital'" 1537 1538 cmds = [ ( """create temp table del_org as 1539 select id from dem.org 1540 where description in(%s) or 1541 id_category in ( select id from dem.org_category c 1542 where c.description in (%s)) 1543 """ % (nameList, categoryList), [] ), 1544 ("""create temp table del_identity as 1545 select id from dem.identity 1546 where id in 1547 ( 1548 select id_identity from dem.lnk_person_org_address 1549 where id_org in ( select id from del_org) 1550 )""",[] ), 1551 ("""create temp table del_comm as 1552 (select id_comm from dem.lnk_org2comm_channel where 1553 id_org in ( select id from del_org) 1554 ) UNION 1555 (select id_comm from dem.lnk_identity2comm_chan where 1556 id_identity in ( select id from del_identity) 1557 )""", [] ), 1558 ("""delete from dem.names where id_identity in 1559 (select id from del_identity)""",[]), 1560 ("""delete from dem.lnk_person_org_address where 1561 id_org in (select id from del_org )""",[]), 1562 ("""delete from dem.lnk_person_org_address where 1563 id_identity in (select id from del_identity)""", []), 1564 ("""delete from dem.lnk_org2comm_channel 1565 where id_org in (select id from del_org) """,[]), 1566 ("""delete from dem.lnk_identity2comm_chan 1567 where id_identity in (select id from del_identity)""",[] ), 1568 ("""delete from dem.comm_channel where id in ( select id_comm from del_comm)""",[]), 1569 ("""delete from dem.lnk_job2person where id_identity in (select id from del_identity)""", []), 1570 ("""delete from dem.identity where id in (select id from del_identity)""",[] ), 1571 ("""delete from dem.org where id in ( select id from del_org) """ , [] ), 1572 ("""drop table del_comm""",[]), 1573 ("""drop table del_identity""",[]), 1574 ("""drop table del_org""", []) 1575 1576 ] 1577 result = gmPG.run_commit("personalia", cmds) <> None 1578 1579 return result
1580 1581
1582 - def login_user_and_test(logintest, service = 'personalia', msg = "failed test" , use_prefix_rw= False):
1583 """ tries to get and verify a read-write connection 1584 which has permission to write to org tables, so the test case 1585 can run. 1586 """ 1587 login2 = gmPG.request_login_params() 1588 1589 #login as the RW user 1590 p = gmPG.ConnectionPool( login2) 1591 if use_prefix_rw: 1592 conn = p.GetConnection( service, readonly = 0) 1593 else: 1594 conn = p.GetConnection(service) 1595 result = logintest(conn) 1596 1597 if result is False: 1598 print msg 1599 1600 p.ReleaseConnection(service) 1601 return result, login2
1602
1603 - def test_rw_user(conn):
1604 # test it is a RW user, by making a entry and deleting it 1605 try: 1606 c.reload("org_category") 1607 cursor = conn.cursor() 1608 1609 cursor.execute("select last_value from dem.org_id_seq") 1610 [org_id_seq] = cursor.fetchone() 1611 1612 cursor.execute(""" 1613 insert into dem.org ( description, id_category, id) 1614 values ( 'xxxDEFAULTxxx', %d, 1615 %d) 1616 """ % ( c.getId('org_category', 'hospital') , org_id_seq + 1 ) ) 1617 cursor.execute(""" 1618 delete from dem.org where id = %d""" % ( org_id_seq + 1) ) 1619 # make sure this exercise is committed, else a deadlock will occur 1620 conn.commit() 1621 except: 1622 _log.exception("Test of Update Permission failed") 1623 return False 1624 return True
1625
1626 - def test_admin_user(conn):
1627 try: 1628 cursor = conn.cursor() 1629 1630 cursor.execute("select last_value from dem.org_category_id_seq") 1631 [org_cat_id_seq] = cursor.fetchone() 1632 1633 cursor.execute(""" 1634 insert into dem.org_category ( description, id) 1635 values ( 'xxxDEFAULTxxx',%d) 1636 """ % (org_cat_id_seq + 1 ) ) 1637 cursor.execute(""" 1638 delete from dem.org_category where description like 'xxxDEFAULTxxx' """ ) 1639 # make sure this exercise is committed, else a deadlock will occur 1640 conn.commit() 1641 except: 1642 _log.exception("Test of Update Permission failed") 1643 return False 1644 return True
1645
1646 - def login_rw_user():
1647 return login_user_and_test( test_rw_user, "login cannot update org", use_prefix_rw = True)
1648 1649
1650 - def login_admin_user():
1651 return login_user_and_test( test_admin_user, "login cannot update org_category" )
1652 1653
1654 - def create_temp_categories( categories = ['hospital']):
1655 print "NEED TO CREATE TEMPORARY ORG_CATEGORY.\n\n ** PLEASE ENTER administrator login : e.g user 'gm-dbo' and his password" 1656 #get a admin login 1657 for i in xrange(0, 4): 1658 result ,tmplogin = login_admin_user() 1659 if result: 1660 break 1661 if i == 4: 1662 print "Failed to login" 1663 return categories 1664 1665 # and save it , for later removal of test categories. 1666 from Gnumed.pycommon import gmLoginInfo 1667 adminlogin = gmLoginInfo.LoginInfo(*tmplogin.GetInfo()) 1668 1669 #login as admin 1670 p = gmPG.ConnectionPool( tmplogin) 1671 conn = p.GetConnection("personalia") 1672 1673 # use the last value + 1 of the relevant sequence, but don't increment it 1674 cursor = conn.cursor() 1675 1676 failed_categories = [] 1677 n =1 1678 for cat in categories: 1679 cursor.execute("select last_value from dem.org_category_id_seq") 1680 [org_cat_id_seq] = cursor.fetchone() 1681 1682 cursor.execute( "insert into dem.org_category(description, id) values('%s', %d)" % (cat, org_cat_id_seq + n) ) 1683 cursor.execute("select id from dem.org_category where description in ('%s')" % cat) 1684 1685 result = cursor.fetchone() 1686 if result == None or len(result) == 0: 1687 failed_categories.append(cat) 1688 print "Failed insert of category", cat 1689 conn.rollback() 1690 else: 1691 conn.commit() 1692 n += 1 1693 1694 conn.commit() 1695 p.ReleaseConnection('personalia') 1696 return failed_categories, adminlogin
1697
1698 - def clean_org_categories(adminlogin = None, categories = ['hospital'], service='personalia'):
1699 1700 print""" 1701 1702 The temporary category(s) will now 1703 need to be removed under an administrator login 1704 e.g. gm-dbo 1705 Please enter login for administrator: 1706 """ 1707 if adminlogin is None: 1708 for i in xrange(0, 4): 1709 result, adminlogin = login_admin_user() 1710 if result: 1711 break 1712 if i == 4: 1713 print "FAILED TO LOGIN" 1714 return categories 1715 1716 p = gmPG.ConnectionPool(adminlogin) 1717 conn = p.GetConnection(service) 1718 failed_remove = [] 1719 for cat in categories: 1720 try: 1721 cursor = conn.cursor() 1722 cursor.execute( "delete from dem.org_category where description in ('%s')"%cat) 1723 conn.commit() 1724 cursor.execute("select id from dem.org_category where description in ('%s')"%cat) 1725 if cursor.fetchone() == None: 1726 print "Succeeded in removing temporary org_category" 1727 else: 1728 print "*** Unable to remove temporary org_category" 1729 failed_remove .append(cat) 1730 except: 1731 import sys 1732 print sys.exc_info()[0], sys.exc_info()[1] 1733 import traceback 1734 traceback.print_tb(sys.exc_info()[2]) 1735 1736 failed_remove.append(cat) 1737 1738 conn = None 1739 p.ReleaseConnection(service) 1740 if failed_remove <> []: 1741 print "FAILED TO REMOVE ", failed_remove 1742 return failed_remove
1743
1744 - def test_CatFinder():
1745 print "TESTING cCatFinder" 1746 1747 print """c = cCatFinder("org_category")""" 1748 c = cCatFinder("org_category") 1749 1750 print c.getCategories("org_category") 1751 1752 print """c = cCatFinder("enum_comm_types")""" 1753 c = cCatFinder("enum_comm_types") 1754 1755 l = c.getCategories("enum_comm_types") 1756 print "testing getId()" 1757 l2 = [] 1758 for x in l: 1759 l2.append((x, c.getId("enum_comm_types", x))) 1760 print l2 1761 1762 print """testing borg behaviour of cCatFinder""" 1763 1764 print c.getCategories("org_category")
1765 1766
1767 - def help():
1768 print """\nNB If imports not found , try: 1769 1770 change to gnumed/client directory , then 1771 1772 export PYTHONPATH=$PYTHONPATH:../;python business/gmOrganization.py 1773 1774 --clean , cleans the test data and categories 1775 1776 --gui sets up as for no arguments, then runs the client. 1777 on normal exit of client, normal tests run, and 1778 then cleanup of entered data. 1779 1780 using the gui, 1781 1782 the 'list organisations' toolbar button , loads all organisations 1783 in the database, and display suborgs and persons associated 1784 with each organisation. 1785 1786 the 'add organisation' button will add a top-level organisation. 1787 the 'add branch/division' button will work when the last selected 1788 org was a top level org. 1789 1790 the 'add person M|F' button works if an org is selected. 1791 1792 the save button works when entry is finished. 1793 1794 selecting on an item, will bring it into the editing area. 1795 1796 No test yet for dirtied edit data, to query whether to 1797 save or discard. (30/5/2004) 1798 """ 1799 print 1800 print "In the connection query, please enter" 1801 print "a WRITE-ENABLED user e.g. _test-doc (not test-doc), and the right password" 1802 print 1803 print "Run the unit test with cmdline argument '--clean' if trying to clean out test data" 1804 print 1805 1806 print """You can get a sermon by running 1807 export PYTHONPATH=$PYTHONPATH:../;python business/gmOrganization.py --sermon 1808 """ 1809 print """ 1810 Pre-requisite data in database is : 1811 gnumed=# select * from org_category ; 1812 id | description 1813 ----+------------- 1814 1 | hospital 1815 (1 row) 1816 1817 gnumed=# select * from enum_comm_types ; 1818 id | description 1819 ----+------------- 1820 1 | email 1821 2 | fax 1822 3 | homephone 1823 4 | workphone 1824 5 | mobile 1825 6 | web 1826 7 | jabber 1827 (7 rows) 1828 """
1829
1830 - def sermon():
1831 print""" 1832 This test case shows how many things can go wrong , even with just a test case. 1833 Problem areas include: 1834 - postgres administration : pg_ctl state, pg_hba.conf, postgres.conf config files . 1835 - 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. 1836 1837 1838 - 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 1839 without calling the backend for each object. 1840 1841 - error and exception handling - at what point in the call stack to handle an error. 1842 Better to use error return values and log exceptions near where they occur, vs. wrapping inside try: except: blocks and catching typed exceptions. 1843 1844 1845 - test-case construction: test data is needed often, and the issue 1846 is whether it is better to keep the test data volatile in the test-case, 1847 which handles both its creation and deletion, or to add it to test data 1848 server configuration files, which may involve running backend scripts 1849 for loading and removing test data. 1850 1851 1852 1853 - Database connection problems: 1854 -Is the problem in : 1855 - pg_ctl start -D /...mydata-directory is wrong, and gnumed isn't existing there. 1856 1857 - ..mydata-directory/pg_hba.conf 1858 - can psql connect locally and remotely with the username and password. 1859 - Am I using md5 authenentication and I've forgotten the password. 1860 - I need to su postgres, alter pg_hba.conf to use trust for 1861 the gnumed database, pg_ctl restart -D .., su normal_user, psql gnumed, alter user my_username password 'doh' 1862 - might be helpful: the default password for _test-doc is test-doc 1863 1864 - ../mydata-directory/postgres.conf 1865 - tcp connect flag isn't set to true 1866 1867 - remote/local mixup : 1868 a different set of user passwords on different hosts. e.g the password 1869 for _test-doc is 'pass' on localhost and 'test-doc' for the serverhost. 1870 - In the prompts for admin and user login, local host was used for one, and 1871 remote host for the other 1872 1873 1874 1875 - test data won't go away : 1876 - 'hospital' category in org_category : the test case failed in a previous run 1877 and the test data was left there; now the test case won't try to delete it 1878 because it exists as a pre-existing category; 1879 soln : run with --clean option 1880 1881 1882 - test-case failed unexpectedly, or break key was hit in the middle of a test-case run. 1883 Soln: run with --clean option, 1884 1885 1886 """
1887 1888 1889 #============================================================ 1890 1891 import sys 1892 testgui = False 1893 if len(sys.argv) > 1: 1894 if sys.argv[1] == '--clean': 1895 result = clean_test_org() 1896 p = gmPG.ConnectionPool() 1897 p.ReleaseConnection('personalia') 1898 if result: 1899 print "probably succeeded in cleaning orgs" 1900 else: print "failed to clean orgs" 1901 1902 clean_org_categories() 1903 sys.exit(1) 1904 1905 if sys.argv[1] == "--sermon": 1906 sermon() 1907 1908 if sys.argv[1] == "--help": 1909 help() 1910 1911 if sys.argv[1] =="--gui": 1912 testgui = True 1913 1914 print "*" * 50 1915 print "RUNNING UNIT TEST of gmOrganization " 1916 1917 1918 test_CatFinder() 1919 tmp_category = False # tmp_category means test data will need to be added and removed 1920 # for org_category . 1921 1922 c = cCatFinder() 1923 if not "hospital" in c.getCategories("org_category") : 1924 print "FAILED in prerequisite for org_category : test categories are not present." 1925 1926 tmp_category = True 1927 1928 if tmp_category: 1929 # test data in a categorical table (restricted access) is needed 1930 1931 print """You will need to switch login identity to database administrator in order 1932 to have permission to write to the org_category table, 1933 and then switch back to the ordinary write-enabled user in order 1934 to run the test cases. 1935 Finally you will need to switch back to administrator login to 1936 remove the temporary org_categories. 1937 """ 1938 categories = ['hospital'] 1939 result, adminlogin = create_temp_categories(categories) 1940 if result == categories: 1941 print "Unable to create temporary org_category. Test aborted" 1942 sys.exit(-1) 1943 if result <> []: 1944 print "UNABLE TO CREATE THESE CATEGORIES" 1945 if not raw_input("Continue ?") in ['y', 'Y'] : 1946 sys.exit(-1) 1947 1948 try: 1949 results = [] 1950 if tmp_category: 1951 print "succeeded in creating temporary org_category" 1952 print 1953 print "** Now ** RESUME LOGIN ** of write-enabled user (e.g. _test-doc) " 1954 while (1): 1955 # get the RW user for org tables (again) 1956 if login_rw_user(): 1957 break 1958 1959 if testgui: 1960 if cCatFinder().getId('org_category','hospital') == None: 1961 print "Needed to set up temporary org_category 'hospital" 1962 sys.exit(-1) 1963 import os 1964 print os.environ['PWD'] 1965 os.spawnl(os.P_WAIT, "/usr/bin/python", "/usr/bin/python","wxpython/gnumed.py", "--debug") 1966 1967 #os.popen2('python client/wxpython/gnumed.py --debug') 1968 1969 # run the test case 1970 results = testOrg() 1971 1972 # cleanup after the test case 1973 for (result , org) in results: 1974 if not result and org.getId() <> None: 1975 print "trying cleanup" 1976 if org.shallow_del(): print " may have succeeded" 1977 else: 1978 print "May need manual removal of org id =", org.getId() 1979 1980 testOrgPersons() 1981 1982 testListOrgs() 1983 1984 except: 1985 import sys 1986 print sys.exc_info()[0], sys.exc_info()[1] 1987 _log.exception( "Fatal exception") 1988 1989 # clean-up any temporary categories. 1990 if tmp_category: 1991 try: 1992 clean_org_categories(adminlogin) 1993 except: 1994 while(not login_rw_user()[0]): 1995 pass 1996 clean_test_org() 1997 clean_org_categories(adminlogin) 1998 1999 2000
2001 -def setPostcodeWidgetFromUrbId(postcodeWidget, id_urb):
2002 """convenience method for urb and postcode phrasewheel interaction. 2003 never called without both arguments, but need to check that id_urb 2004 is not invalid""" 2005 #TODO type checking that the postcodeWidget is a phrasewheel configured 2006 # with a postcode matcher 2007 if postcodeWidget is None or id_urb is None: 2008 return False 2009 postcode = getPostcodeForUrbId(id_urb) 2010 if postcode is None: 2011 return False 2012 if len(postcode) == 0: 2013 return True 2014 postcodeWidget.SetValue(postcode) 2015 postcodeWidget.input_was_selected= 1 2016 return True
2017 2018 #------------------------------------------------------------ 2019
2020 -def setUrbPhraseWheelFromPostcode(pwheel, postcode):
2021 """convenience method for common postcode to urb phrasewheel collaboration. 2022 there is no default args for these utility functions, 2023 This function is never called without both arguments, otherwise 2024 there is no intention (= modify the urb phrasewheel with postcode value). 2025 """ 2026 # TODO type checking that the pwheel is a urb phrasewheel with a urb matcher 2027 # clearing post code unsets target 2028 # phrasewheel's postcode context 2029 if pwheel is None: 2030 return False 2031 if postcode == '': 2032 pwheel.set_context("postcode", "%") 2033 return True 2034 urbs = getUrbsForPostcode(postcode) 2035 if urbs is None: 2036 return False 2037 if len(urbs) == 0: 2038 return True 2039 pwheel.SetValue(urbs[0]) 2040 pwheel.input_was_selected = 1 2041 2042 # FIXME: once the postcode context is set, 2043 # the urb phrasewheel will only return urbs with 2044 # the same postcode. These can be viewed by clearing 2045 # the urb widget. ?How to unset the postcode context, 2046 # some gui gesture ? clearing the postcode 2047 # (To view all the urbs for a set context, 2048 # put a "*" in the urb box and activate the picklist. 2049 # THE PROBLEM WITH THIS IS IF THE USER CLEARS THE BOX AND SET CONTEXT IS RESET, 2050 # then the "*" will try to pull all thousands of urb names, freezing the app. 2051 # so needs a fixup (? have SQL select ... LIMIT n in Phrasewheel ) 2052 2053 pwheel.set_context("postcode", postcode) 2054 return True
2055 2056 #====================================================================== 2057