Code

gadfly backend now complete - can handle schema changes in non-Multilinks
[roundup.git] / roundup / backends / back_anydbm.py
index 2a338a0bb7ba250a088606c5c0ab9c454726638c..91f8a76a4221d7ee7be304f19af475493bdf89e1 100644 (file)
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 # 
-#$Id: back_anydbm.py,v 1.73 2002-09-10 00:11:49 richard Exp $
+#$Id: back_anydbm.py,v 1.79 2002-09-15 23:06:20 richard Exp $
 '''
 This module defines a backend that saves the hyperdatabase in a database
 chosen by anydbm. It is guaranteed to always be available in python
@@ -121,7 +121,10 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'getclass', (self, classname)
-        return self.classes[classname]
+        try:
+            return self.classes[classname]
+        except KeyError:
+            raise KeyError, 'There is no class called "%s"'%classname
 
     #
     # Class DBs
@@ -154,7 +157,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         if os.path.exists(path):
             db_type = whichdb.whichdb(path)
             if not db_type:
-                raise hyperdb.DatabaseError, "Couldn't identify database type"
+                raise DatabaseError, "Couldn't identify database type"
         elif os.path.exists(path+'.db'):
             # if the path ends in '.db', it's a dbm database, whether
             # anydbm says it's dbhash or not!
@@ -182,7 +185,7 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         try:
             dbm = __import__(db_type)
         except ImportError:
-            raise hyperdb.DatabaseError, \
+            raise DatabaseError, \
                 "Couldn't open database - the required module '%s'"\
                 " is not available"%db_type
         if __debug__:
@@ -447,7 +450,8 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
     #
     # Journal
     #
-    def addjournal(self, classname, nodeid, action, params):
+    def addjournal(self, classname, nodeid, action, params, creator=None,
+            creation=None):
         ''' Journal the Action
         'action' may be:
 
@@ -457,9 +461,9 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         '''
         if __debug__:
             print >>hyperdb.DEBUG, 'addjournal', (self, classname, nodeid,
-                action, params)
+                action, params, creator, creation)
         self.transactions.append((self.doSaveJournal, (classname, nodeid,
-            action, params)))
+            action, params, creator, creation)))
 
     def getjournal(self, classname, nodeid):
         ''' get the journal for id
@@ -603,28 +607,22 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
             self.databases[db_name] = self.opendb(db_name, 'c')
         return self.databases[db_name]
 
-    def doSaveJournal(self, classname, nodeid, action, params):
-        # handle supply of the special journalling parameters (usually
-        # supplied on importing an existing database)
+    def doSaveJournal(self, classname, nodeid, action, params, creator,
+            creation):
+        # serialise the parameters now if necessary
         if isinstance(params, type({})):
-            if params.has_key('creator'):
-                journaltag = self.user.get(params['creator'], 'username')
-                del params['creator']
-            else:
-                journaltag = self.journaltag
-            if params.has_key('created'):
-                journaldate = params['created'].serialise()
-                del params['created']
-            else:
-                journaldate = date.Date().serialise()
-            if params.has_key('activity'):
-                del params['activity']
-
-            # serialise the parameters now
             if action in ('set', 'create'):
                 params = self.serialise(classname, params)
+
+        # handle supply of the special journalling parameters (usually
+        # supplied on importing an existing database)
+        if creator:
+            journaltag = creator
         else:
             journaltag = self.journaltag
+        if creation:
+            journaldate = creation.serialise()
+        else:
             journaldate = date.Date().serialise()
 
         # create the journal entry
@@ -678,6 +676,11 @@ class Database(FileStorage, hyperdb.Database, roundupdb.Database):
         self.destroyednodes = {}
         self.transactions = []
 
+    def close(self):
+        ''' Nothing to do
+        '''
+        pass
+
 _marker = []
 class Class(hyperdb.Class):
     '''The handle to a particular class of nodes in a hyperdatabase.'''
@@ -884,7 +887,9 @@ class Class(hyperdb.Class):
             proptype = properties[prop]
             value = self.get(nodeid, prop)
             # "marshal" data where needed
-            if isinstance(proptype, hyperdb.Date):
+            if value is None:
+                pass
+            elif isinstance(proptype, hyperdb.Date):
                 value = value.get_tuple()
             elif isinstance(proptype, hyperdb.Interval):
                 value = value.get_tuple()
@@ -919,6 +924,9 @@ class Class(hyperdb.Class):
             if propname == 'id':
                 newid = value
                 continue
+            elif value is None:
+                # don't set Nones
+                continue
             elif isinstance(prop, hyperdb.Date):
                 value = date.Date(value)
             elif isinstance(prop, hyperdb.Interval):
@@ -927,12 +935,26 @@ class Class(hyperdb.Class):
                 pwd = password.Password()
                 pwd.unpack(value)
                 value = pwd
-            if value is not None:
-                d[propname] = value
+            d[propname] = value
 
-        # add
+        # extract the extraneous journalling gumpf and nuke it
+        if d.has_key('creator'):
+            creator = d['creator']
+            del d['creator']
+        else:
+            creator = None
+        if d.has_key('creation'):
+            creation = d['creation']
+            del d['creation']
+        else:
+            creation = None
+        if d.has_key('activity'):
+            del d['activity']
+
+        # add the node and journal
         self.db.addnode(self.classname, newid, d)
-        self.db.addjournal(self.classname, newid, 'create', d)
+        self.db.addjournal(self.classname, newid, 'create', d, creator,
+            creation)
         return newid
 
     def get(self, nodeid, propname, default=_marker, cache=1):
@@ -976,14 +998,9 @@ class Class(hyperdb.Class):
                 raise ValueError, 'Journalling is disabled for this class'
             journal = self.db.getjournal(self.classname, nodeid)
             if journal:
-                name = self.db.getjournal(self.classname, nodeid)[0][2]
+                return self.db.getjournal(self.classname, nodeid)[0][2]
             else:
-                return None
-            try:
-                return self.db.user.lookup(name)
-            except KeyError:
-                # the journaltag user doesn't exist any more
-                return None
+                return self.db.journaltag
 
         # get the property (raises KeyErorr if invalid)
         prop = self.properties[propname]
@@ -1006,7 +1023,7 @@ class Class(hyperdb.Class):
 
         return d[propname]
 
-    # XXX not in spec
+    # not in spec
     def getnode(self, nodeid, cache=1):
         ''' Return a convenience wrapper for the node.
 
@@ -1359,7 +1376,7 @@ class Class(hyperdb.Class):
         otherwise a KeyError is raised.
         '''
         if not self.key:
-            raise TypeError, 'No key property set'
+            raise TypeError, 'No key property set for class %s'%self.classname
         cldb = self.db.getclassdb(self.classname)
         try:
             for nodeid in self.db.getnodeids(self.classname, cldb):
@@ -1373,7 +1390,7 @@ class Class(hyperdb.Class):
             cldb.close()
         raise KeyError, keyvalue
 
-    # XXX: change from spec - allows multiple props to match
+    # change from spec - allows multiple props to match
     def find(self, **propspec):
         '''Get the ids of nodes in this class which link to the given nodes.
 
@@ -1734,7 +1751,9 @@ class Class(hyperdb.Class):
             d['id'] = String()
             d['creation'] = hyperdb.Date()
             d['activity'] = hyperdb.Date()
-            d['creator'] = hyperdb.Link("user")
+            # can't be a link to user because the user might have been
+            # retired since the journal entry was created
+            d['creator'] = hyperdb.String()
         return d
 
     def addprop(self, **properties):
@@ -1823,7 +1842,7 @@ class FileClass(Class):
 
         # extract the "content" property from the proplist
         i = propnames.index('content')
-        content = proplist[i]
+        content = eval(proplist[i])
         del propnames[i]
         del proplist[i]
 
@@ -1884,7 +1903,7 @@ class FileClass(Class):
         self.db.indexer.add_text((self.classname, nodeid, 'content'), content,
             mime_type)
 
-# XXX deviation from spec - was called ItemClass
+# deviation from spec - was called ItemClass
 class IssueClass(Class, roundupdb.IssueClass):
     # Overridden methods:
     def __init__(self, db, classname, **properties):