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