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