remove old file
[sfa.git] / changes / Sites.py
1 from types import StringTypes
2 import string
3
4 from PLC.Faults import *
5 from PLC.Parameter import Parameter, Mixed
6 from PLC.Filter import Filter
7 from PLC.Debug import profile
8 from PLC.Table import Row, Table
9 from PLC.Slices import Slice, Slices
10 from PLC.PCUs import PCU, PCUs
11 from PLC.Nodes import Node, Nodes
12 from PLC.Addresses import Address, Addresses
13 from PLC.Persons import Person, Persons
14
15 class Site(Row):
16     """
17     Representation of a row in the sites table. To use, optionally
18     instantiate with a dict of values. Update as you would a
19     dict. Commit to the database with sync().
20     """
21
22     table_name = 'sites'
23     primary_key = 'site_id'
24     join_tables = ['person_site', 'site_address', 'peer_site']
25     fields = {
26         'site_id': Parameter(int, "Site identifier"),
27         'name': Parameter(str, "Full site name", max = 254),
28         'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
29         'login_base': Parameter(str, "Site slice prefix", max = 20),
30         'is_public': Parameter(bool, "Publicly viewable site"),
31         'enabled': Parameter(bool, "Has been enabled"),
32         'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
33         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
34         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
35         'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
36         'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True),
37         'max_slices': Parameter(int, "Maximum number of slices that the site is able to create"),
38         'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create"),
39         'uuid': Parameter(str, "Universal Unique Identifier"),
40         'person_ids': Parameter([int], "List of account identifiers"),
41         'slice_ids': Parameter([int], "List of slice identifiers"),
42         'address_ids': Parameter([int], "List of address identifiers"),
43         'pcu_ids': Parameter([int], "List of PCU identifiers"),
44         'node_ids': Parameter([int], "List of site node identifiers"),
45         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
46         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
47         'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
48         }
49     related_fields = {
50         'persons': [Mixed(Parameter(int, "Person identifier"),
51                           Parameter(str, "Email address"))],
52         'addresses': [Mixed(Parameter(int, "Address identifer"),
53                             Filter(Address.fields))]
54         }
55     # for Cache
56     class_key = 'login_base'
57     foreign_fields = ['abbreviated_name', 'name', 'is_public', 'latitude', 'longitude',
58                       'url', 'max_slices', 'max_slivers', 'uuid', 
59                       ]
60     # forget about these ones, they are read-only anyway
61     # handling them causes Cache to re-sync all over again 
62     # 'last_updated', 'date_created'
63     foreign_xrefs = []
64
65     def validate_name(self, name):
66         if not len(name):
67             raise PLCInvalidArgument, "Name must be specified"
68
69         return name
70
71     validate_abbreviated_name = validate_name
72
73     def validate_login_base(self, login_base):
74         if not len(login_base):
75             raise PLCInvalidArgument, "Login base must be specified"
76
77         if not set(login_base).issubset(string.lowercase + string.digits):
78             raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters or numbers"
79
80         conflicts = Sites(self.api, [login_base])
81         for site in conflicts:
82             if 'site_id' not in self or self['site_id'] != site['site_id']:
83                 raise PLCInvalidArgument, "login_base already in use"
84
85         return login_base
86
87     def validate_latitude(self, latitude):
88         if not self.has_key('longitude') or \
89            self['longitude'] is None:
90             raise PLCInvalidArgument, "Longitude must also be specified"
91
92         return latitude
93
94     def validate_longitude(self, longitude):
95         if not self.has_key('latitude') or \
96            self['latitude'] is None:
97             raise PLCInvalidArgument, "Latitude must also be specified"
98
99         return longitude
100
101     validate_date_created = Row.validate_timestamp
102     validate_last_updated = Row.validate_timestamp
103
104     add_person = Row.add_object(Person, 'person_site')
105     remove_person = Row.remove_object(Person, 'person_site')
106
107     add_address = Row.add_object(Address, 'site_address')
108     remove_address = Row.remove_object(Address, 'site_address')
109
110     def update_last_updated(self, commit = True):
111         """
112         Update last_updated field with current time
113         """
114
115         assert 'site_id' in self
116         assert self.table_name
117
118         self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
119                        " where site_id = %d" % (self['site_id']) )
120         self.sync(commit)    
121
122
123     def associate_persons(self, auth, field, value):
124         """
125         Adds persons found in value list to this site (using AddPersonToSite).
126         Deletes persons not found in value list from this site (using DeletePersonFromSite).
127         """
128         
129         assert 'person_ids' in self
130         assert 'site_id' in self
131         assert isinstance(value, list)
132
133         (person_ids, emails) = self.separate_types(value)[0:2]
134
135         # Translate emails into person_ids
136         if emails:
137             persons = Persons(self.api, emails, ['person_id']).dict('person_id')
138             person_ids += persons.keys()
139
140         # Add new ids, remove stale ids
141         if self['person_ids'] != person_ids:
142             from PLC.Methods.AddPersonToSite import AddPersonToSite
143             from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
144             new_persons = set(person_ids).difference(self['person_ids'])
145             stale_persons = set(self['person_ids']).difference(person_ids)
146          
147             for new_person in new_persons:
148                 AddPersonToSite.__call__(AddPersonToSite(self.api), auth, new_person, self['site_id'])
149             for stale_person in stale_persons:
150                 DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, stale_person, self['site_id'])              
151
152     def associate_addresses(self, auth, field, value):
153         """
154         Deletes addresses_ids not found in value list (using DeleteAddress).  
155         Adds address if slice_fields w/o address_id found in value list (using AddSiteAddress).
156         Update address if slice_fields w/ address_id found in value list (using UpdateAddress).
157         """
158         
159         assert 'address_ids' in self
160         assert 'site_id' in self
161         assert isinstance(value, list)
162
163         (address_ids, blank, addresses) = self.separate_types(value)
164
165         for address in addresses:
166             if 'address_id' in address:
167                 address_ids.append(address['address_id'])               
168
169         # Add new ids, remove stale ids
170         if self['address_ids'] != address_ids:
171             from PLC.Methods.DeleteAddress import DeleteAddress
172             stale_addresses = set(self['address_ids']).difference(address_ids)
173
174             for stale_address in stale_addresses:
175                 DeleteAddress.__call__(DeleteAddress(self.api), auth, stale_address)    
176         
177         if addresses:
178             from PLC.Methods.AddSiteAddress import AddSiteAddress
179             from PLC.Methods.UpdateAddress import UpdateAddress
180                 
181             updated_addresses = filter(lambda address: 'address_id' in address, addresses)
182             added_addresses = filter(lambda address: 'address_id' not in address, addresses)
183                 
184             for address in added_addresses:
185                 AddSiteAddress.__call__(AddSiteAddress(self.api), auth, self['site_id'], address)       
186             for address in updated_addresses:
187                 address_id = address.pop('address_id')
188                 UpdateAddress.__call__(UpdateAddress(self.api), auth, address_id, address)
189
190     def delete(self, commit = True):
191         """
192         Delete existing site.
193         """
194
195         assert 'site_id' in self
196
197         # Delete accounts of all people at the site who are not
198         # members of at least one other non-deleted site.
199         persons = Persons(self.api, self['person_ids'])
200         for person in persons:
201             delete = True
202
203             person_sites = Sites(self.api, person['site_ids'])
204             for person_site in person_sites:
205                 if person_site['site_id'] != self['site_id']:
206                     delete = False
207                     break
208
209             if delete:
210                 person.delete(commit = False)
211
212         # Delete all site addresses
213         addresses = Addresses(self.api, self['address_ids'])
214         for address in addresses:
215             address.delete(commit = False)
216
217         # Delete all site slices
218         slices = Slices(self.api, self['slice_ids'])
219         for slice in slices:
220             slice.delete(commit = False)
221
222         # Delete all site PCUs
223         pcus = PCUs(self.api, self['pcu_ids'])
224         for pcu in pcus:
225             pcu.delete(commit = False)
226
227         # Delete all site nodes
228         nodes = Nodes(self.api, self['node_ids'])
229         for node in nodes:
230             node.delete(commit = False)
231
232         # Clean up miscellaneous join tables
233         for table in self.join_tables:
234             self.api.db.do("DELETE FROM %s WHERE site_id = %d" % \
235                            (table, self['site_id']))
236
237         # Mark as deleted
238         self['deleted'] = True
239         self.sync(commit)
240
241 class Sites(Table):
242     """
243     Representation of row(s) from the sites table in the
244     database.
245     """
246
247     def __init__(self, api, site_filter = None, columns = None):
248         Table.__init__(self, api, Site, columns)
249
250         sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
251               ", ".join(self.columns)
252
253         if site_filter is not None:
254             if isinstance(site_filter, (list, tuple, set)):
255                 # Separate the list into integers and strings
256                 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
257                 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
258                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
259                 sql += " AND (%s) %s" % site_filter.sql(api, "OR")
260             elif isinstance(site_filter, dict):
261                 site_filter = Filter(Site.fields, site_filter)
262                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
263             elif isinstance (site_filter, StringTypes):
264                 site_filter = Filter(Site.fields, {'login_base':[site_filter]})
265                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
266             elif isinstance (site_filter, int):
267                 site_filter = Filter(Site.fields, {'site_id':[site_filter]})
268                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
269             else:
270                 raise PLCInvalidArgument, "Wrong site filter %r"%site_filter
271
272         self.selectall(sql)