- fix can_update and return value
[plcapi.git] / PLC / Sites.py
index aac4a6d..b345a04 100644 (file)
@@ -19,6 +19,8 @@ class Site(Row):
     dict. Commit to the database with sync().
     """
 
+    table_name = 'sites'
+    primary_key = 'site_id'
     fields = {
         'site_id': Parameter(int, "Site identifier"),
         'name': Parameter(str, "Full site name", max = 254),
@@ -28,30 +30,35 @@ class Site(Row):
         'latitude': Parameter(float, "Decimal latitude of the site", min = -90.0, max = 90.0),
         'longitude': Parameter(float, "Decimal longitude of the site", min = -180.0, max = 180.0),
         'url': Parameter(str, "URL of a page that describes the site", max = 254),
-        'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch"),
-        'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch"),
-        'deleted': Parameter(bool, "Has been deleted"),
+        'date_created': Parameter(int, "Date and time when site entry was created, in seconds since UNIX epoch", ro = True),
+        'last_updated': Parameter(int, "Date and time when site entry was last updated, in seconds since UNIX epoch", ro = True),
         'max_slices': Parameter(int, "Maximum number of slices that the site is able to create"),
         'max_slivers': Parameter(int, "Maximum number of slivers that the site is able to create"),
-        'person_ids': Parameter([int], "List of account identifiers"),
-        'slice_ids': Parameter([int], "List of slice identifiers"),
-        'address_ids': Parameter([int], "List of address identifiers"),
-        # 'pcu_ids': Parameter([int], "List of PCU identifiers"),
-        'node_ids': Parameter([int], "List of site node identifiers"),
+        'person_ids': Parameter([int], "List of account identifiers", ro = True),
+        'slice_ids': Parameter([int], "List of slice identifiers", ro = True),
+        'address_ids': Parameter([int], "List of address identifiers", ro = True),
+        'pcu_ids': Parameter([int], "List of PCU identifiers", ro = True),
+        'node_ids': Parameter([int], "List of site node identifiers", ro = True),
         }
 
-    def __init__(self, api, fields):
-        Row.__init__(self, fields)
-        self.api = api
+    def validate_name(self, name):
+        if not len(name):
+            raise PLCInvalidArgument, "Name must be specified"
+
+        return name
+
+    validate_abbreviated_name = validate_name
 
     def validate_login_base(self, login_base):
-        if not set(login_base).issubset(string.ascii_letters):
-            raise PLCInvalidArgument, "Login base must consist only of ASCII letters"
+        if not len(login_base):
+            raise PLCInvalidArgument, "Login base must be specified"
+
+        if not set(login_base).issubset(string.ascii_letters.lower()):
+            raise PLCInvalidArgument, "Login base must consist only of lowercase ASCII letters"
 
-        login_base = login_base.lower()
         conflicts = Sites(self.api, [login_base])
         for site_id, site in conflicts.iteritems():
-            if not site['deleted'] and ('site_id' not in self or self['site_id'] != site_id):
+            if 'site_id' not in self or self['site_id'] != site_id:
                 raise PLCInvalidArgument, "login_base already in use"
 
         return login_base
@@ -81,17 +88,18 @@ class Site(Row):
 
         site_id = self['site_id']
         person_id = person['person_id']
-        self.api.db.do("INSERT INTO person_site (person_id, site_id)" \
-                       " VALUES(%(person_id)d, %(site_id)d)",
-                       locals())
 
-        if commit:
-            self.api.db.commit()
+        if person_id not in self['person_ids']:
+            assert site_id not in person['site_ids']
 
-        if 'person_ids' in self and person_id not in self['person_ids']:
-            self['person_ids'].append(person_id)
+            self.api.db.do("INSERT INTO person_site (person_id, site_id)" \
+                           " VALUES(%(person_id)d, %(site_id)d)",
+                           locals())
+
+            if commit:
+                self.api.db.commit()
 
-        if 'site_ids' in person and site_id not in person['site_ids']:
+            self['person_ids'].append(person_id)
             person['site_ids'].append(site_id)
 
     def remove_person(self, person, commit = True):
@@ -105,72 +113,65 @@ class Site(Row):
 
         site_id = self['site_id']
         person_id = person['person_id']
-        self.api.db.do("DELETE FROM person_site" \
-                       " WHERE person_id = %(person_id)d" \
-                       " AND site_id = %(site_id)d",
-                       locals())
 
-        if commit:
-            self.api.db.commit()
+        if person_id in self['person_ids']:
+            assert site_id in person['site_ids']
 
-        if 'person_ids' in self and person_id in self['person_ids']:
-            self['person_ids'].remove(person_id)
+            self.api.db.do("DELETE FROM person_site" \
+                           " WHERE person_id = %(person_id)d" \
+                           " AND site_id = %(site_id)d",
+                           locals())
+
+            if commit:
+                self.api.db.commit()
 
-        if 'site_ids' in person and site_id in person['site_ids']:
+            self['person_ids'].remove(person_id)
             person['site_ids'].remove(site_id)
 
-    def sync(self, commit = True):
+    def add_address(self, address, commit = True):
+        """
+        Add address to existing site.
+        """
+
+        assert 'site_id' in self
+        assert isinstance(address, Address)
+        assert 'address_id' in address
+
+        site_id = self['site_id']
+        address_id = address['address_id']
+
+        if address_id not in self['address_ids']:
+            self.api.db.do("INSERT INTO site_address (address_id, site_id)" \
+                           " VALUES(%(address_id)d, %(site_id)d)",
+                           locals())
+
+            if commit:
+                self.api.db.commit()
+
+            self['address_ids'].append(address_id)
+
+    def remove_address(self, address, commit = True):
         """
-        Flush changes back to the database.
+        Remove address from existing site.
         """
 
-        self.validate()
-
-        try:
-            if not self['name'] or \
-               not self['abbreviated_name'] or \
-               not self['login_base']:
-                raise KeyError
-        except KeyError:
-            raise PLCInvalidArgument, "name, abbreviated_name, and login_base must all be specified"
-
-        # Fetch a new site_id if necessary
-        if 'site_id' not in self:
-            rows = self.api.db.selectall("SELECT NEXTVAL('sites_site_id_seq') AS site_id")
-            if not rows:
-                raise PLCDBError, "Unable to fetch new site_id"
-            self['site_id'] = rows[0]['site_id']
-            insert = True
-        else:
-            insert = False
-
-        # Filter out fields that cannot be set or updated directly
-        sites_fields = self.api.db.fields('sites')
-        fields = dict(filter(lambda (key, value): key in sites_fields,
-                             self.items()))
-        for ro_field in 'date_created', 'last_updated':
-            if ro_field in fields:
-                del fields[ro_field]
-
-        # Parameterize for safety
-        keys = fields.keys()
-        values = [self.api.db.param(key, value) for (key, value) in fields.items()]
-
-        if insert:
-            # Insert new row in sites table
-            sql = "INSERT INTO sites (%s) VALUES (%s)" % \
-                  (", ".join(keys), ", ".join(values))
-        else:
-            # Update existing row in sites table
-            columns = ["%s = %s" % (key, value) for (key, value) in zip(keys, values)]
-            sql = "UPDATE sites SET " + \
-                  ", ".join(columns) + \
-                  " WHERE site_id = %(site_id)d"
-
-        self.api.db.do(sql, fields)
-
-        if commit:
-            self.api.db.commit()
+        assert 'site_id' in self
+        assert isinstance(address, Address)
+        assert 'address_id' in address
+
+        site_id = self['site_id']
+        address_id = address['address_id']
+
+        if address_id in self['address_ids']:
+            self.api.db.do("DELETE FROM site_address" \
+                           " WHERE address_id = %(address_id)d" \
+                           " AND site_id = %(site_id)d",
+                           locals())
+
+            if commit:
+                self.api.db.commit()
+
+            self['address_ids'].remove(address_id)
 
     def delete(self, commit = True):
         """
@@ -187,8 +188,7 @@ class Site(Row):
 
             person_sites = Sites(self.api, person['site_ids'])
             for person_site_id, person_site in person_sites.iteritems():
-                if person_site_id != self['site_id'] and \
-                   not person_site['deleted']:
+                if person_site_id != self['site_id']:
                     delete = False
                     break
 
@@ -206,9 +206,9 @@ class Site(Row):
            slice.delete(commit = False)
 
         # Delete all site PCUs
-        pcus = PCUs(self.api, self['pcu_ids'])
-        for pcu in pcus.values():
-           pcu.delete(commit = False)
+        pcus = PCUs(self.api, self['pcu_ids'])
+        for pcu in pcus.values():
+           pcu.delete(commit = False)
 
         # Delete all site nodes
         nodes = Nodes(self.api, self['node_ids'])
@@ -232,11 +232,11 @@ class Sites(Table):
     fields.
     """
 
-    def __init__(self, api, site_id_or_login_base_list = None, fields = Site.fields):
+    def __init__(self, api, site_id_or_login_base_list = None):
         self.api = api
 
         sql = "SELECT %s FROM view_sites WHERE deleted IS False" % \
-              ", ".join(fields)
+              ", ".join(Site.fields)
 
         if site_id_or_login_base_list:
             # Separate the list into integers and strings