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