protecting against (future) non-class root node children
[Evergreen.git] / Open-ILS / src / python / oils / org.py
1 import osrf.ses
2 import oils.event, oils.const
3 import sys
4
5 class OrgUtil(object):
6     ''' Collection of general purpose org_unit utility functions '''
7
8     _org_tree = None  
9     _org_types = None  
10     _flat_org_tree = {}
11
12     @staticmethod
13     def _verify_tree():
14         if not OrgUtil._org_tree:
15             OrgUtil.fetch_org_tree()
16
17     @staticmethod
18     def fetch_org_tree():
19         ''' Returns the whole org_unit tree '''
20
21         if OrgUtil._org_tree:
22             return OrgUtil._org_tree
23
24         tree = osrf.ses.ClientSession.atomic_request(
25             oils.const.OILS_APP_ACTOR,
26             'open-ils.actor.org_tree.retrieve')
27
28         oils.event.Event.parse_and_raise(tree)
29         OrgUtil._org_tree = tree
30         OrgUtil.flatten_org_tree(tree)
31         return tree
32
33     @staticmethod
34     def flatten_org_tree(node):
35         ''' Creates links from an ID-based hash to the org units in the org tree '''
36         if not node:
37             node = OrgUtil._org_tree
38         OrgUtil._flat_org_tree[int(node.id())] = node
39         for child in node.children():
40             OrgUtil.flatten_org_tree(child)
41
42     @staticmethod
43     def get_org_unit(org_id):
44         OrgUtil._verify_tree()
45         if isinstance(org_id, osrf.net_obj.NetworkObject):
46             return org_id
47         return OrgUtil._flat_org_tree[int(org_id)]
48         
49
50     @staticmethod
51     def fetch_org_types():
52         ''' Returns the list of org_unit_type objects '''
53
54         if OrgUtil._org_types:
55             return OrgUtil._org_types
56
57         types = osrf.ses.ClientSession.atomic_request(
58             oils.const.OILS_APP_ACTOR, 'open-ils.actor.org_types.retrieve')
59
60         oils.event.Event.parse_and_raise(types)
61         OrgUtil._org_types = types
62         return types
63
64
65     @staticmethod
66     def get_org_type(org_unit):
67         ''' Given an org_unit, this returns the org_unit_type object it's linked to '''
68         types = OrgUtil.fetch_org_types()
69         return [t for t in types if t.id() == org_unit.ou_type()][0]
70
71
72     @staticmethod
73     def get_related_tree(org_unit):
74         ''' Returns a cloned tree of orgs including all ancestors and 
75             descendants of the provided org '''
76
77         OrgUtil._verify_tree()
78         org = org_unit = OrgUtil.get_org_unit(org_unit.id()).shallow_clone()
79         while org.parent_ou():
80             parent = org.parent_ou()
81             if not isinstance(parent, osrf.net_obj.NetworkObject):
82                 parent = OrgUtil._flat_org_tree[parent]
83             parent = parent.shallow_clone()
84             parent.children([org])
85             org = parent
86         root = org
87
88         def trim_org(node):
89             node = node.shallow_clone()
90             children = node.children()
91             if len(children) > 0:
92                 node.children([])
93                 for child in children:
94                     node.children().append(trim_org(child))
95             return node
96
97         trim_org(org_unit)
98         return root
99
100     @staticmethod
101     def get_union_tree(org_id_list):
102         ''' Returns the smallest org tree which encompases all of the orgs in org_id_list '''
103
104         OrgUtil._verify_tree()
105         if len(org_id_list) == 0:
106             return None
107         main_tree = OrgUtil.get_related_tree(OrgUtil.get_org_unit(org_id_list[0]))
108
109         if len(org_id_list) == 1:
110             return main_tree
111
112         for org in org_id_list[1:]:
113             node = OrgUtil.get_related_tree(OrgUtil.get_org_unit(org))
114             main_node = main_tree
115
116             while node.id() == main_node.id():
117                 child = node.children()[0]
118                 main_child_node = main_node.children()[0]
119                 child.parent_ou(node)
120                 main_child_node.parent_ou(main_node)
121                 node = child
122                 main_node = main_child_node
123
124             main_node.parent_ou().children().append(node)
125
126         return main_tree
127
128     @staticmethod
129     def get_related_list(org_unit):
130         ''' Returns a flat list of related org_units '''
131         OrgUtil._verify_tree()
132         tree = OrgUtil.get_related_tree(org_unit)
133         orglist = []
134         def flatten(node):
135             orglist.append(node)
136             for child in node.children():
137                 flatten(child)
138         flatten(tree)
139         return orglist
140
141     @staticmethod
142     def get_min_depth(org_id_list):
143         ''' Returns the minimun depth (highest tree position) of all orgs in the list '''
144         depth = None
145         for org in org_id_list:
146             new_depth = OrgUtil.get_org_type(OrgUtil.get_org_unit(org)).depth()
147             if depth is None:
148                 depth = new_depth
149             elif new_depth < depth:
150                 depth = new_depth
151         return depth
152
153     @staticmethod
154     def debug_tree(org_unit, indent=0):
155         ''' Simple function to print the tree of orgs provided '''
156         for i in range(indent):
157             sys.stdout.write('_')
158         print '%s id=%s depth=%s' % (org_unit.shortname(), str(org_unit.id()), str(OrgUtil.get_org_type(org_unit).depth()))
159         indent += 1
160         for child in org_unit.children():
161             OrgUtil.debug_tree(child, indent)
162         
163