1 from types import StringTypes
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
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().
23 primary_key = 'site_id'
24 join_tables = ['person_site', 'site_address', 'peer_site']
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 = 32),
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 'person_ids': Parameter([int], "List of account identifiers"),
40 'slice_ids': Parameter([int], "List of slice identifiers"),
41 'address_ids': Parameter([int], "List of address identifiers"),
42 'pcu_ids': Parameter([int], "List of PCU identifiers"),
43 'node_ids': Parameter([int], "List of site node identifiers"),
44 'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
45 'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
46 'site_tag_ids' : Parameter ([int], "List of tags attached to this site"),
47 'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
50 'persons': [Mixed(Parameter(int, "Person identifier"),
51 Parameter(str, "Email address"))],
52 'addresses': [Mixed(Parameter(int, "Address identifer"),
53 Filter(Address.fields))]
55 view_tags_name = "view_site_tags"
56 # tags are used by the Add/Get/Update methods to expose tags
57 # this is initialized here and updated by the accessors factory
60 def validate_name(self, name):
62 raise PLCInvalidArgument("Name must be specified")
66 validate_abbreviated_name = validate_name
68 def validate_login_base(self, login_base):
69 if not len(login_base):
70 raise PLCInvalidArgument("Login base must be specified")
72 if not set(login_base).issubset(string.lowercase + string.digits + '.'):
73 raise PLCInvalidArgument("Login base must consist only of lowercase ASCII letters or numbers or dots")
75 conflicts = Sites(self.api, [login_base])
76 for site in conflicts:
77 if 'site_id' not in self or self['site_id'] != site['site_id']:
78 raise PLCInvalidArgument("login_base already in use")
82 def validate_latitude(self, latitude):
83 if 'longitude' not in self or \
84 self['longitude'] is None:
85 raise PLCInvalidArgument("Longitude must also be specified")
89 def validate_longitude(self, longitude):
90 if 'latitude' not in self or \
91 self['latitude'] is None:
92 raise PLCInvalidArgument("Latitude must also be specified")
96 validate_date_created = Row.validate_timestamp
97 validate_last_updated = Row.validate_timestamp
99 add_person = Row.add_object(Person, 'person_site')
100 remove_person = Row.remove_object(Person, 'person_site')
102 add_address = Row.add_object(Address, 'site_address')
103 remove_address = Row.remove_object(Address, 'site_address')
105 def update_last_updated(self, commit = True):
107 Update last_updated field with current time
110 assert 'site_id' in self
111 assert self.table_name
113 self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
114 " where site_id = %d" % (self['site_id']) )
118 def associate_persons(self, auth, field, value):
120 Adds persons found in value list to this site (using AddPersonToSite).
121 Deletes persons not found in value list from this site (using DeletePersonFromSite).
124 assert 'person_ids' in self
125 assert 'site_id' in self
126 assert isinstance(value, list)
128 (person_ids, emails) = self.separate_types(value)[0:2]
130 # Translate emails into person_ids
132 persons = Persons(self.api, emails, ['person_id']).dict('person_id')
133 person_ids += list(persons.keys())
135 # Add new ids, remove stale ids
136 if self['person_ids'] != person_ids:
137 from PLC.Methods.AddPersonToSite import AddPersonToSite
138 from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
139 new_persons = set(person_ids).difference(self['person_ids'])
140 stale_persons = set(self['person_ids']).difference(person_ids)
142 for new_person in new_persons:
143 AddPersonToSite.__call__(AddPersonToSite(self.api), auth, new_person, self['site_id'])
144 for stale_person in stale_persons:
145 DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, stale_person, self['site_id'])
147 def associate_addresses(self, auth, field, value):
149 Deletes addresses_ids not found in value list (using DeleteAddress).
150 Adds address if slice_fields w/o address_id found in value list (using AddSiteAddress).
151 Update address if slice_fields w/ address_id found in value list (using UpdateAddress).
154 assert 'address_ids' in self
155 assert 'site_id' in self
156 assert isinstance(value, list)
158 (address_ids, blank, addresses) = self.separate_types(value)
160 for address in addresses:
161 if 'address_id' in address:
162 address_ids.append(address['address_id'])
164 # Add new ids, remove stale ids
165 if self['address_ids'] != address_ids:
166 from PLC.Methods.DeleteAddress import DeleteAddress
167 stale_addresses = set(self['address_ids']).difference(address_ids)
169 for stale_address in stale_addresses:
170 DeleteAddress.__call__(DeleteAddress(self.api), auth, stale_address)
173 from PLC.Methods.AddSiteAddress import AddSiteAddress
174 from PLC.Methods.UpdateAddress import UpdateAddress
176 updated_addresses = [address for address in addresses if 'address_id' in address]
177 added_addresses = [address for address in addresses if 'address_id' not in address]
179 for address in added_addresses:
180 AddSiteAddress.__call__(AddSiteAddress(self.api), auth, self['site_id'], address)
181 for address in updated_addresses:
182 address_id = address.pop('address_id')
183 UpdateAddress.__call__(UpdateAddress(self.api), auth, address_id, address)
185 def delete(self, commit = True):
187 Delete existing site.
190 assert 'site_id' in self
192 # Delete accounts of all people at the site who are not
193 # members of at least one other non-deleted site.
194 persons = Persons(self.api, self['person_ids'])
195 for person in persons:
198 person_sites = Sites(self.api, person['site_ids'])
199 for person_site in person_sites:
200 if person_site['site_id'] != self['site_id']:
205 person.delete(commit = False)
207 # Delete all site addresses
208 addresses = Addresses(self.api, self['address_ids'])
209 for address in addresses:
210 address.delete(commit = False)
212 # Delete all site slices
213 slices = Slices(self.api, self['slice_ids'])
215 slice.delete(commit = False)
217 # Delete all site PCUs
218 pcus = PCUs(self.api, self['pcu_ids'])
220 pcu.delete(commit = False)
222 # Delete all site nodes
223 nodes = Nodes(self.api, self['node_ids'])
225 node.delete(commit = False)
227 # Clean up miscellaneous join tables
228 for table in self.join_tables:
229 self.api.db.do("DELETE FROM %s WHERE site_id = %d" % \
230 (table, self['site_id']))
233 self['deleted'] = True
238 Representation of row(s) from the sites table in the
242 def __init__(self, api, site_filter = None, columns = None):
243 Table.__init__(self, api, Site, columns)
246 for tagname in self.tag_columns:
247 view= "%s left join %s using (%s)"%(view,Site.tagvalue_view_name(tagname),
250 sql = "SELECT %s FROM %s WHERE deleted IS False" % \
251 (", ".join(list(self.columns.keys())+list(self.tag_columns.keys())),view)
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 = [x for x in site_filter if isinstance(x, int)]
257 strs = [x for x in site_filter if isinstance(x, StringTypes)]
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 allowed_fields=dict(list(Site.fields.items())+list(Site.tags.items()))
262 site_filter = Filter(allowed_fields, site_filter)
263 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
264 elif isinstance (site_filter, StringTypes):
265 site_filter = Filter(Site.fields, {'login_base':site_filter})
266 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
267 elif isinstance (site_filter, int):
268 site_filter = Filter(Site.fields, {'site_id':site_filter})
269 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
271 raise PLCInvalidArgument("Wrong site filter %r"%site_filter)