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