the PLCAPI class requires __construct as well in order to move to php8
[plcapi.git] / PLC / Sites.py
index 6157ca5..f9f16bc 100644 (file)
@@ -1,8 +1,7 @@
-from types import StringTypes
 import string
 
 from PLC.Faults import *
 import string
 
 from PLC.Faults import *
-from PLC.Parameter import Parameter
+from PLC.Parameter import Parameter, Mixed
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
 from PLC.Filter import Filter
 from PLC.Debug import profile
 from PLC.Table import Row, Table
@@ -26,10 +25,10 @@ class Site(Row):
         'site_id': Parameter(int, "Site identifier"),
         'name': Parameter(str, "Full site name", max = 254),
         'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
         'site_id': Parameter(int, "Site identifier"),
         'name': Parameter(str, "Full site name", max = 254),
         'abbreviated_name': Parameter(str, "Abbreviated site name", max = 50),
-        'login_base': Parameter(str, "Site slice prefix", max = 20),
+        'login_base': Parameter(str, "Site slice prefix", max = 32),
         'is_public': Parameter(bool, "Publicly viewable site"),
         'enabled': Parameter(bool, "Has been enabled"),
         'is_public': Parameter(bool, "Publicly viewable site"),
         'enabled': Parameter(bool, "Has been enabled"),
-       'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
+        'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0, nullok = True),
         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
         'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0, nullok = True),
         'url': Parameter(str, "URL of a page that describes the site", max = 254, nullok = True),
         'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
@@ -43,22 +42,23 @@ class Site(Row):
         'node_ids': Parameter([int], "List of site node identifiers"),
         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
         'node_ids': Parameter([int], "List of site node identifiers"),
         'peer_id': Parameter(int, "Peer to which this site belongs", nullok = True),
         'peer_site_id': Parameter(int, "Foreign site identifier at peer", nullok = True),
-       'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
+        'site_tag_ids' : Parameter ([int], "List of tags attached to this site"),
+        'ext_consortium_id': Parameter(int, "external consortium id", nullok = True)
         }
         }
-
-    # for Cache
-    class_key = 'login_base'
-    foreign_fields = ['abbreviated_name', 'name', 'is_public', 'latitude', 'longitude',
-                     'url', 'max_slices', 'max_slivers',
-                     ]
-    # forget about these ones, they are read-only anyway
-    # handling them causes Cache to re-sync all over again 
-    # 'last_updated', 'date_created'
-    foreign_xrefs = []
+    related_fields = {
+        'persons': [Mixed(Parameter(int, "Person identifier"),
+                          Parameter(str, "Email address"))],
+        'addresses': [Mixed(Parameter(int, "Address identifer"),
+                            Filter(Address.fields))]
+        }
+    view_tags_name = "view_site_tags"
+    # tags are used by the Add/Get/Update methods to expose tags
+    # this is initialized here and updated by the accessors factory
+    tags = { }
 
     def validate_name(self, name):
         if not len(name):
 
     def validate_name(self, name):
         if not len(name):
-            raise PLCInvalidArgument, "Name must be specified"
+            raise PLCInvalidArgument("Name must be specified")
 
         return name
 
 
         return name
 
@@ -66,29 +66,29 @@ class Site(Row):
 
     def validate_login_base(self, login_base):
         if not len(login_base):
 
     def validate_login_base(self, login_base):
         if not len(login_base):
-            raise PLCInvalidArgument, "Login base must be specified"
+            raise PLCInvalidArgument("Login base must be specified")
 
 
-        if not set(login_base).issubset(string.lowercase + string.digits):
-            raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters or numbers"
+        if not set(login_base).issubset(string.ascii_lowercase + string.digits + '.'):
+            raise PLCInvalidArgument("Login base must consist only of lowercase ASCII letters or numbers or dots")
 
         conflicts = Sites(self.api, [login_base])
         for site in conflicts:
             if 'site_id' not in self or self['site_id'] != site['site_id']:
 
         conflicts = Sites(self.api, [login_base])
         for site in conflicts:
             if 'site_id' not in self or self['site_id'] != site['site_id']:
-                raise PLCInvalidArgument, "login_base already in use"
+                raise PLCInvalidArgument("login_base already in use")
 
         return login_base
 
     def validate_latitude(self, latitude):
 
         return login_base
 
     def validate_latitude(self, latitude):
-        if not self.has_key('longitude') or \
+        if 'longitude' not in self or \
            self['longitude'] is None:
            self['longitude'] is None:
-            raise PLCInvalidArgument, "Longitude must also be specified"
+            raise PLCInvalidArgument("Longitude must also be specified")
 
         return latitude
 
     def validate_longitude(self, longitude):
 
         return latitude
 
     def validate_longitude(self, longitude):
-        if not self.has_key('latitude') or \
+        if 'latitude' not in self or \
            self['latitude'] is None:
            self['latitude'] is None:
-            raise PLCInvalidArgument, "Latitude must also be specified"
+            raise PLCInvalidArgument("Latitude must also be specified")
 
         return longitude
 
 
         return longitude
 
@@ -111,7 +111,75 @@ class Site(Row):
 
         self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
                        " where site_id = %d" % (self['site_id']) )
 
         self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \
                        " where site_id = %d" % (self['site_id']) )
-        self.sync(commit)    
+        self.sync(commit)
+
+
+    def associate_persons(self, auth, field, value):
+        """
+        Adds persons found in value list to this site (using AddPersonToSite).
+        Deletes persons not found in value list from this site (using DeletePersonFromSite).
+        """
+
+        assert 'person_ids' in self
+        assert 'site_id' in self
+        assert isinstance(value, list)
+
+        (person_ids, emails) = self.separate_types(value)[0:2]
+
+        # Translate emails into person_ids
+        if emails:
+            persons = Persons(self.api, emails, ['person_id']).dict('person_id')
+            person_ids += list(persons.keys())
+
+        # Add new ids, remove stale ids
+        if self['person_ids'] != person_ids:
+            from PLC.Methods.AddPersonToSite import AddPersonToSite
+            from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite
+            new_persons = set(person_ids).difference(self['person_ids'])
+            stale_persons = set(self['person_ids']).difference(person_ids)
+
+            for new_person in new_persons:
+                AddPersonToSite.__call__(AddPersonToSite(self.api), auth, new_person, self['site_id'])
+            for stale_person in stale_persons:
+                DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, stale_person, self['site_id'])
+
+    def associate_addresses(self, auth, field, value):
+        """
+        Deletes addresses_ids not found in value list (using DeleteAddress).
+        Adds address if slice_fields w/o address_id found in value list (using AddSiteAddress).
+        Update address if slice_fields w/ address_id found in value list (using UpdateAddress).
+        """
+
+        assert 'address_ids' in self
+        assert 'site_id' in self
+        assert isinstance(value, list)
+
+        (address_ids, blank, addresses) = self.separate_types(value)
+
+        for address in addresses:
+            if 'address_id' in address:
+                address_ids.append(address['address_id'])
+
+        # Add new ids, remove stale ids
+        if self['address_ids'] != address_ids:
+            from PLC.Methods.DeleteAddress import DeleteAddress
+            stale_addresses = set(self['address_ids']).difference(address_ids)
+
+            for stale_address in stale_addresses:
+                DeleteAddress.__call__(DeleteAddress(self.api), auth, stale_address)
+
+        if addresses:
+            from PLC.Methods.AddSiteAddress import AddSiteAddress
+            from PLC.Methods.UpdateAddress import UpdateAddress
+
+            updated_addresses = [address for address in addresses if 'address_id' in address]
+            added_addresses = [address for address in addresses if 'address_id' not in address]
+
+            for address in added_addresses:
+                AddSiteAddress.__call__(AddSiteAddress(self.api), auth, self['site_id'], address)
+            for address in updated_addresses:
+                address_id = address.pop('address_id')
+                UpdateAddress.__call__(UpdateAddress(self.api), auth, address_id, address)
 
     def delete(self, commit = True):
         """
 
     def delete(self, commit = True):
         """
@@ -173,26 +241,32 @@ class Sites(Table):
     def __init__(self, api, site_filter = None, columns = None):
         Table.__init__(self, api, Site, columns)
 
     def __init__(self, api, site_filter = None, columns = None):
         Table.__init__(self, api, Site, columns)
 
-        sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
-              ", ".join(self.columns)
+        view = "view_sites"
+        for tagname in self.tag_columns:
+            view= "%s left join %s using (%s)"%(view,Site.tagvalue_view_name(tagname),
+                                                Site.primary_key)
+
+        sql = "SELECT %s FROM %s WHERE deleted IS False" % \
+            (", ".join(list(self.columns.keys())+list(self.tag_columns.keys())),view)
 
         if site_filter is not None:
             if isinstance(site_filter, (list, tuple, set)):
                 # Separate the list into integers and strings
 
         if site_filter is not None:
             if isinstance(site_filter, (list, tuple, set)):
                 # Separate the list into integers and strings
-                ints = filter(lambda x: isinstance(x, (int, long)), site_filter)
-                strs = filter(lambda x: isinstance(x, StringTypes), site_filter)
+                ints = [x for x in site_filter if isinstance(x, int)]
+                strs = [x for x in site_filter if isinstance(x, str)]
                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
                 sql += " AND (%s) %s" % site_filter.sql(api, "OR")
             elif isinstance(site_filter, dict):
                 site_filter = Filter(Site.fields, {'site_id': ints, 'login_base': strs})
                 sql += " AND (%s) %s" % site_filter.sql(api, "OR")
             elif isinstance(site_filter, dict):
-                site_filter = Filter(Site.fields, site_filter)
+                allowed_fields=dict(list(Site.fields.items())+list(Site.tags.items()))
+                site_filter = Filter(allowed_fields, site_filter)
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
-            elif isinstance (site_filter, StringTypes):
-                site_filter = Filter(Site.fields, {'login_base':[site_filter]})
+            elif isinstance (site_filter, str):
+                site_filter = Filter(Site.fields, {'login_base':site_filter})
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             elif isinstance (site_filter, int):
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             elif isinstance (site_filter, int):
-                site_filter = Filter(Site.fields, {'site_id':[site_filter]})
+                site_filter = Filter(Site.fields, {'site_id':site_filter})
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             else:
                 sql += " AND (%s) %s" % site_filter.sql(api, "AND")
             else:
-                raise PLCInvalidArgument, "Wrong site filter %r"%site_filter
+                raise PLCInvalidArgument("Wrong site filter %r"%site_filter)
 
         self.selectall(sql)
 
         self.selectall(sql)