3 from types import StringTypes
6 from PLC.Faults import *
7 from PLC.Parameter import Parameter, Mixed
8 from PLC.Filter import Filter
9 from PLC.Debug import profile
10 from PLC.Table import Row, Table
11 from PLC.Slices import Slice, Slices
12 from PLC.PCUs import PCU, PCUs
13 from PLC.Nodes import Node, Nodes
14 from PLC.Addresses import Address, Addresses
15 from PLC.Persons import Person, Persons
19 Representation of a row in the sites table. To use, optionally
20 instantiate with a dict of values. Update as you would a
21 dict. Commit to the database with sync().
25 primary_key = 'site_id'
26 join_tables = ['person_site', 'site_address', 'peer_site']
28 'site_id': Parameter(int, "Site identifier"),
29 'name': Parameter(str, "Full site name", max = 254),
30 'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
31 'login_base': Parameter(str, "Site slice prefix", max = 20),
32 'is_public': Parameter(bool, "Publicly viewable site"),
33 'enabled': Parameter(bool, "Has been enabled"),
34 'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
35 'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
36 'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
37 'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
38 'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True),
39 'max_slices': Parameter(int, "Maximum number of slices that the site is able to create"),
40 'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create"),
41 'person_ids': Parameter([int], "List of account identifiers"),
42 'slice_ids': Parameter([int], "List of slice identifiers"),
43 'address_ids': Parameter([int], "List of address identifiers"),
44 'pcu_ids': Parameter([int], "List of PCU identifiers"),
45 'node_ids': Parameter([int], "List of site node identifiers"),
46 'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
47 'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
48 'site_tag_ids' : Parameter ([int], "List of tags attached to this site"),
49 'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
52 'persons': [Mixed(Parameter(int, "Person identifier"),
53 Parameter(str, "Email address"))],
54 'addresses': [Mixed(Parameter(int, "Address identifer"),
55 Filter(Address.fields))]
57 view_tags_name = "view_site_tags"
58 # tags are used by the Add/Get/Update methods to expose tags
59 # this is initialized here and updated by the accessors factory
62 def validate_name(self, name):
64 raise PLCInvalidArgument, "Name must be specified"
68 validate_abbreviated_name = validate_name
70 def validate_login_base(self, login_base):
71 if not len(login_base):
72 raise PLCInvalidArgument, "Login base must be specified"
74 if not set(login_base).issubset(string.lowercase + string.digits):
75 raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters or numbers"
77 conflicts = Sites(self.api, [login_base])
78 for site in conflicts:
79 if 'site_id' not in self or self['site_id'] != site['site_id']:
80 raise PLCInvalidArgument, "login_base already in use"
84 def validate_latitude(self, latitude):
85 if not self.has_key('longitude') or \
86 self['longitude'] is None:
87 raise PLCInvalidArgument, "Longitude must also be specified"
91 def validate_longitude(self, longitude):
92 if not self.has_key('latitude') or \
93 self['latitude'] is None:
94 raise PLCInvalidArgument, "Latitude must also be specified"
98 validate_date_created = Row.validate_timestamp
99 validate_last_updated = Row.validate_timestamp
101 add_person = Row.add_object(Person, 'person_site')
102 remove_person = Row.remove_object(Person, 'person_site')
104 add_address = Row.add_object(Address, 'site_address')
105 remove_address = Row.remove_object(Address, 'site_address')
107 def update_last_updated(self, commit = True):
109 Update last_updated field with current time
112 assert 'site_id' in self
113 assert self.table_name
115 self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
116 " where site_id = %d" % (self['site_id']) )
120 def associate_persons(self, auth, field, value):
122 Adds persons found in value list to this site (using AddPersonToSite).
123 Deletes persons not found in value list from this site (using DeletePersonFromSite).
126 assert 'person_ids' in self
127 assert 'site_id' in self
128 assert isinstance(value, list)
130 (person_ids, emails) = self.separate_types(value)[0:2]
132 # Translate emails into person_ids
134 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
135 person_ids += persons.keys()
137 # Add new ids, remove stale ids
138 if self['person_ids'] != person_ids:
139 from PLC.Methods.AddPersonToSite import AddPersonToSite
140 from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
141 new_persons = set(person_ids).difference(self['person_ids'])
142 stale_persons = set(self['person_ids']).difference(person_ids)
144 for new_person in new_persons:
145 AddPersonToSite.__call__(AddPersonToSite(self.api), auth, new_person, self['site_id'])
146 for stale_person in stale_persons:
147 DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, stale_person, self['site_id'])
149 def associate_addresses(self, auth, field, value):
151 Deletes addresses_ids not found in value list (using DeleteAddress).
152 Adds address if slice_fields w/o address_id found in value list (using AddSiteAddress).
153 Update address if slice_fields w/ address_id found in value list (using UpdateAddress).
156 assert 'address_ids' in self
157 assert 'site_id' in self
158 assert isinstance(value, list)
160 (address_ids, blank, addresses) = self.separate_types(value)
162 for address in addresses:
163 if 'address_id' in address:
164 address_ids.append(address['address_id'])
166 # Add new ids, remove stale ids
167 if self['address_ids'] != address_ids:
168 from PLC.Methods.DeleteAddress import DeleteAddress
169 stale_addresses = set(self['address_ids']).difference(address_ids)
171 for stale_address in stale_addresses:
172 DeleteAddress.__call__(DeleteAddress(self.api), auth, stale_address)
175 from PLC.Methods.AddSiteAddress import AddSiteAddress
176 from PLC.Methods.UpdateAddress import UpdateAddress
178 updated_addresses = filter(lambda address: 'address_id' in address, addresses)
179 added_addresses = filter(lambda address: 'address_id' not in address, addresses)
181 for address in added_addresses:
182 AddSiteAddress.__call__(AddSiteAddress(self.api), auth, self['site_id'], address)
183 for address in updated_addresses:
184 address_id = address.pop('address_id')
185 UpdateAddress.__call__(UpdateAddress(self.api), auth, address_id, address)
187 def delete(self, commit = True):
189 Delete existing site.
192 assert 'site_id' in self
194 # Delete accounts of all people at the site who are not
195 # members of at least one other non-deleted site.
196 persons = Persons(self.api, self['person_ids'])
197 for person in persons:
200 person_sites = Sites(self.api, person['site_ids'])
201 for person_site in person_sites:
202 if person_site['site_id'] != self['site_id']:
207 person.delete(commit = False)
209 # Delete all site addresses
210 addresses = Addresses(self.api, self['address_ids'])
211 for address in addresses:
212 address.delete(commit = False)
214 # Delete all site slices
215 slices = Slices(self.api, self['slice_ids'])
217 slice.delete(commit = False)
219 # Delete all site PCUs
220 pcus = PCUs(self.api, self['pcu_ids'])
222 pcu.delete(commit = False)
224 # Delete all site nodes
225 nodes = Nodes(self.api, self['node_ids'])
227 node.delete(commit = False)
229 # Clean up miscellaneous join tables
230 for table in self.join_tables:
231 self.api.db.do("DELETE FROM %s WHERE site_id = %d" % \
232 (table, self['site_id']))
235 self['deleted'] = True
240 Representation of row(s) from the sites table in the
244 def __init__(self, api, site_filter = None, columns = None):
245 Table.__init__(self, api, Site, columns)
248 for tagname in self.tag_columns:
249 view= "%s left join %s using (%s)"%(view,Site.tagvalue_view_name(tagname),
252 sql = "SELECT %s FROM %s WHERE deleted IS False" % \
253 (", ".join(self.columns.keys()+self.tag_columns.keys()),view)
255 if site_filter is not None:
256 if isinstance(site_filter, (list, tuple, set)):
257 # Separate the list into integers and strings
258 ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
259 strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
260 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
261 sql += " AND (%s) %s" % site_filter.sql(api, "OR")
262 elif isinstance(site_filter, dict):
263 site_filter = Filter(Site.fields, site_filter)
264 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
265 elif isinstance (site_filter, StringTypes):
266 site_filter = Filter(Site.fields, {'login_base':[site_filter]})
267 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
268 elif isinstance (site_filter, int):
269 site_filter = Filter(Site.fields, {'site_id':[site_filter]})
270 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
272 raise PLCInvalidArgument, "Wrong site filter %r"%site_filter