summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: b1b80b4)
raw | patch | inline | side by side (parent: b1b80b4)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 2 Apr 2004 05:58:45 +0000 (05:58 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Fri, 2 Apr 2004 05:58:45 +0000 (05:58 +0000) |
Need to check setting of activity in RDBMS imports.
Metakit import is quite possibly very busted in setjournal() - I didn't
even try to figure how to *clear the previous journal* for the journal
being imported.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2246 57a73879-2fb5-44c3-a270-3262357dd7e2
Metakit import is quite possibly very busted in setjournal() - I didn't
even try to figure how to *clear the previous journal* for the journal
being imported.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2246 57a73879-2fb5-44c3-a270-3262357dd7e2
diff --git a/CHANGES.txt b/CHANGES.txt
index 0b321a824549bd066482f130b6c6329322870eee..e04a1cc5296c4027f40e45d297100d20a7e1bd2e 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
places (thanks Toby Sargeant)
- MySQL and Postgresql use BOOL/BOOLEAN for Boolean types
- OTK generation was busted (thanks Stuart D. Gathman)
+- export and import now include journals (incompatible with export < 0.7)
2004-03-27 0.7.0b2
diff --git a/roundup/admin.py b/roundup/admin.py
index e89f8643df326727cf15b1142a8be69d564fa09b..6c3502e5062b61a93fc2da9ed140c5c3bfb676ba 100644 (file)
--- a/roundup/admin.py
+++ b/roundup/admin.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: admin.py,v 1.63 2004-03-21 23:39:08 richard Exp $
+# $Id: admin.py,v 1.64 2004-04-02 05:58:43 richard Exp $
'''Administration commands for maintaining Roundup trackers.
'''
# do all the classes specified
for classname in classes:
cl = self.get_class(classname)
+
f = open(os.path.join(dir, classname+'.csv'), 'w')
writer = rcsv.writer(f, rcsv.colon_separated)
+
properties = cl.getprops()
propnames = properties.keys()
propnames.sort()
fields.append('is retired')
writer.writerow(fields)
- # all nodes for this class (not using list() 'cos it doesn't
- # include retired nodes)
-
- for nodeid in self.db.getclass(classname).getnodeids():
- # get the regular props
- writer.writerow (cl.export_list(propnames, nodeid))
+ # all nodes for this class
+ for nodeid in cl.getnodeids():
+ writer.writerow(cl.export_list(propnames, nodeid))
# close this file
f.close()
+
+ # export the journals
+ jf = open(os.path.join(dir, classname+'-journals.csv'), 'w')
+ journals = rcsv.writer(jf, rcsv.colon_separated)
+ map(journals.writerow, cl.export_journals())
+ jf.close()
return 0
def do_import(self, args):
Import a database from the directory containing CSV files, one per
class to import.
- The files must define the same properties as the class (including having
- a "header" line with those property names.)
+ The files must define the same properties as the class (including
+ having a "header" line with those property names.)
The imported nodes will have the same nodeid as defined in the
import file, thus replacing any existing content.
from roundup import hyperdb
for file in os.listdir(args[0]):
+ classname, ext = os.path.splitext(file)
# we only care about CSV files
- if not file.endswith('.csv'):
+ if ext != '.csv' or classname.endswith('-journals'):
continue
- f = open(os.path.join(args[0], file))
-
- # get the classname
- classname = os.path.splitext(file)[0]
+ cl = self.get_class(classname)
# ensure that the properties and the CSV file headings match
- cl = self.get_class(classname)
+ f = open(os.path.join(args[0], file))
reader = rcsv.reader(f, rcsv.colon_separated)
file_props = None
maxid = 1
-
# loop through the file and create a node for each entry
for r in reader:
if file_props is None:
file_props = r
continue
-
# do the import and figure the current highest nodeid
maxid = max(maxid, int(cl.import_list(file_props, r)))
+ f.close()
+
+ # import the journals
+ f = open(os.path.join(args[0], classname + '-journals.csv'))
+ reader = rcsv.reader(f, rcsv.colon_separated)
+ cl.import_journals(reader)
+ f.close()
# set the id counter
print 'setting', classname, maxid+1
self.db.setid(classname, str(maxid+1))
+
return 0
def do_pack(self, args):
index 6fb115d5ae1da2555e7cec64177affc3060259cf..0d3d15f0a9ac98479678267208b2b5738339272c 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: back_anydbm.py,v 1.139 2004-03-19 04:47:59 richard Exp $
+#$Id: back_anydbm.py,v 1.140 2004-04-02 05:58:43 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
versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
self.transactions.append((self.doSaveJournal, (classname, nodeid,
action, params, creator, creation)))
+ def setjournal(self, classname, nodeid, journal):
+ '''Set the journal to the "journal" list.'''
+ if __debug__:
+ print >>hyperdb.DEBUG, 'setjournal', (self, classname, nodeid,
+ journal)
+ self.transactions.append((self.doSetJournal, (classname, nodeid,
+ journal)))
+
def getjournal(self, classname, nodeid):
''' get the journal for id
db[nodeid] = marshal.dumps(l)
+ def doSetJournal(self, classname, nodeid, journal):
+ l = []
+ for nodeid, journaldate, journaltag, action, params in journal:
+ # serialise the parameters now if necessary
+ if isinstance(params, type({})):
+ if action in ('set', 'create'):
+ params = self.serialise(classname, params)
+ l.append((nodeid, journaldate, journaltag, action, params))
+ db = self.getCachedJournalDB(classname)
+ db[nodeid] = marshal.dumps(l)
+
def doDestroyNode(self, classname, nodeid):
if __debug__:
print >>hyperdb.DEBUG, 'doDestroyNode', (self, classname, nodeid)
return newid
- def export_list(self, propnames, nodeid):
- ''' Export a node - generate a list of CSV-able data in the order
- specified by propnames for the given node.
- '''
- properties = self.getprops()
- l = []
- for prop in propnames:
- proptype = properties[prop]
- value = self.get(nodeid, prop)
- # "marshal" data where needed
- if value is None:
- pass
- elif isinstance(proptype, hyperdb.Date):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Interval):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Password):
- value = str(value)
- l.append(repr(value))
-
- # append retired flag
- l.append(repr(self.is_retired(nodeid)))
-
- return l
-
- def import_list(self, propnames, proplist):
- ''' Import a node - all information including "id" is present and
- should not be sanity checked. Triggers are not triggered. The
- journal should be initialised using the "creator" and "created"
- information.
-
- Return the nodeid of the node imported.
- '''
- if self.db.journaltag is None:
- raise DatabaseError, 'Database open read-only'
- properties = self.getprops()
-
- # make the new node's property map
- d = {}
- newid = None
- for i in range(len(propnames)):
- # Figure the property for this column
- propname = propnames[i]
-
- # Use eval to reverse the repr() used to output the CSV
- value = eval(proplist[i])
-
- # "unmarshal" where necessary
- if propname == 'id':
- newid = value
- continue
- elif propname == 'is retired':
- # is the item retired?
- if int(value):
- d[self.db.RETIRED_FLAG] = 1
- continue
- elif value is None:
- d[propname] = None
- continue
-
- prop = properties[propname]
- if isinstance(prop, hyperdb.Date):
- value = date.Date(value)
- elif isinstance(prop, hyperdb.Interval):
- value = date.Interval(value)
- elif isinstance(prop, hyperdb.Password):
- pwd = password.Password()
- pwd.unpack(value)
- value = pwd
- d[propname] = value
-
- # get a new id if necessary
- if newid is None:
- newid = self.db.newid(self.classname)
-
- # add the node and journal
- self.db.addnode(self.classname, newid, d)
-
- # extract the journalling stuff 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']
- if d.has_key('actor'):
- del d['actor']
- self.db.addjournal(self.classname, newid, 'create', {}, creator,
- creation)
- return newid
-
def get(self, nodeid, propname, default=_marker, cache=1):
'''Get the value of a property on an existing node of this class.
for react in self.reactors[action]:
react(self.db, self, nodeid, oldvalues)
+ #
+ # import / export support
+ #
+ def export_list(self, propnames, nodeid):
+ ''' Export a node - generate a list of CSV-able data in the order
+ specified by propnames for the given node.
+ '''
+ properties = self.getprops()
+ l = []
+ for prop in propnames:
+ proptype = properties[prop]
+ value = self.get(nodeid, prop)
+ # "marshal" data where needed
+ if value is None:
+ pass
+ elif isinstance(proptype, hyperdb.Date):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Interval):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Password):
+ value = str(value)
+ l.append(repr(value))
+
+ # append retired flag
+ l.append(repr(self.is_retired(nodeid)))
+
+ return l
+
+ def import_list(self, propnames, proplist):
+ ''' Import a node - all information including "id" is present and
+ should not be sanity checked. Triggers are not triggered. The
+ journal should be initialised using the "creator" and "created"
+ information.
+
+ Return the nodeid of the node imported.
+ '''
+ if self.db.journaltag is None:
+ raise DatabaseError, 'Database open read-only'
+ properties = self.getprops()
+
+ # make the new node's property map
+ d = {}
+ newid = None
+ for i in range(len(propnames)):
+ # Figure the property for this column
+ propname = propnames[i]
+
+ # Use eval to reverse the repr() used to output the CSV
+ value = eval(proplist[i])
+
+ # "unmarshal" where necessary
+ if propname == 'id':
+ newid = value
+ continue
+ elif propname == 'is retired':
+ # is the item retired?
+ if int(value):
+ d[self.db.RETIRED_FLAG] = 1
+ continue
+ elif value is None:
+ d[propname] = None
+ continue
+
+ prop = properties[propname]
+ if isinstance(prop, hyperdb.Date):
+ value = date.Date(value)
+ elif isinstance(prop, hyperdb.Interval):
+ value = date.Interval(value)
+ elif isinstance(prop, hyperdb.Password):
+ pwd = password.Password()
+ pwd.unpack(value)
+ value = pwd
+ d[propname] = value
+
+ # get a new id if necessary
+ if newid is None:
+ newid = self.db.newid(self.classname)
+
+ # add the node and journal
+ self.db.addnode(self.classname, newid, d)
+ return newid
+
+ def export_journals(self):
+ '''Export a class's journal - generate a list of lists of
+ CSV-able data:
+
+ nodeid, date, user, action, params
+
+ No heading here - the columns are fixed.
+ '''
+ properties = self.getprops()
+ r = []
+ for nodeid in self.getnodeids():
+ for nodeid, date, user, action, params in self.history(nodeid):
+ date = date.get_tuple()
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ # make sure the params are eval()'able
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = value.get_tuple()
+ elif isinstance(prop, Interval):
+ value = value.get_tuple()
+ elif isinstance(prop, Password):
+ value = str(value)
+ params[propname] = value
+ l = [nodeid, date, user, action, params]
+ r.append(map(repr, l))
+ return r
+
+ def import_journals(self, entries):
+ '''Import a class's journal.
+
+ Uses setjournal() to set the journal for each item.'''
+ properties = self.getprops()
+ d = {}
+ for l in entries:
+ l = map(eval, l)
+ nodeid, date, user, action, params = l
+ r = d.setdefault(nodeid, [])
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = date.Date(value)
+ elif isinstance(prop, Interval):
+ value = date.Interval(value)
+ elif isinstance(prop, Password):
+ pwd = password.Password()
+ pwd.unpack(value)
+ value = pwd
+ params[propname] = value
+ r.append((nodeid, date.Date(date), user, action, params))
+
+ for nodeid, l in d.items():
+ self.db.setjournal(self.classname, nodeid, l)
+
class FileClass(Class, hyperdb.FileClass):
'''This class defines a large chunk of data. To support this, it has a
mandatory String property "content" which is typically saved off
index 17a47e9e02ab3c0af1a167289e912d6fe70df8b0..1de4f530ece1d3b4e572c164e2805e1307f1b6b7 100755 (executable)
-# $Id: back_metakit.py,v 1.69 2004-03-24 05:39:47 richard Exp $
+# $Id: back_metakit.py,v 1.70 2004-04-02 05:58:45 richard Exp $
'''Metakit backend for Roundup, originally by Gordon McMillan.
Known Current Bugs:
action=action,
user = creator,
params = marshal.dumps(params))
+
+ def setjournal(self, tablenm, nodeid, journal):
+ '''Set the journal to the "journal" list.'''
+ tblid = self.tables.find(name=tablenm)
+ if tblid == -1:
+ tblid = self.tables.append(name=tablenm)
+ for nodeid, date, user, action, params in journal:
+ # tableid:I,nodeid:I,date:I,user:I,action:I,params:B
+ self.hist.append(tableid=tblid,
+ nodeid=int(nodeid),
+ date=date,
+ action=action,
+ user=user,
+ params=marshal.dumps(params))
+
def getjournal(self, tablenm, nodeid):
''' get the journal for id
'''
self.db.indexer.add_text((self.classname, nodeid, prop),
str(self.get(nodeid, prop)))
- def export_list(self, propnames, nodeid):
- ''' Export a node - generate a list of CSV-able data in the order
- specified by propnames for the given node.
- '''
- properties = self.getprops()
- l = []
- for prop in propnames:
- proptype = properties[prop]
- value = self.get(nodeid, prop)
- # "marshal" data where needed
- if value is None:
- pass
- elif isinstance(proptype, hyperdb.Date):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Interval):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Password):
- value = str(value)
- l.append(repr(value))
-
- # append retired flag
- l.append(repr(self.is_retired(nodeid)))
-
- return l
-
- def import_list(self, propnames, proplist):
- ''' Import a node - all information including "id" is present and
- should not be sanity checked. Triggers are not triggered. The
- journal should be initialised using the "creator" and "creation"
- information.
-
- Return the nodeid of the node imported.
- '''
- if self.db.journaltag is None:
- raise hyperdb.DatabaseError, 'Database open read-only'
- properties = self.getprops()
-
- d = {}
- view = self.getview(READWRITE)
- for i in range(len(propnames)):
- value = eval(proplist[i])
- if not value:
- continue
-
- propname = propnames[i]
- if propname == 'id':
- newid = value = int(value)
- elif propname == 'is retired':
- # is the item retired?
- if int(value):
- d['_isdel'] = 1
- continue
- elif value is None:
- d[propname] = None
- continue
-
- prop = properties[propname]
- if isinstance(prop, hyperdb.Date):
- value = int(calendar.timegm(value))
- elif isinstance(prop, hyperdb.Interval):
- value = date.Interval(value).serialise()
- elif isinstance(prop, hyperdb.Number):
- value = int(value)
- elif isinstance(prop, hyperdb.Boolean):
- value = int(value)
- elif isinstance(prop, hyperdb.Link) and value:
- value = int(value)
- elif isinstance(prop, hyperdb.Multilink):
- # we handle multilinks separately
- continue
- d[propname] = value
-
- # possibly make a new node
- if not d.has_key('id'):
- d['id'] = newid = self.maxid
- self.maxid += 1
-
- # save off the node
- view.append(d)
-
- # fix up multilinks
- ndx = view.find(id=newid)
- row = view[ndx]
- for i in range(len(propnames)):
- value = eval(proplist[i])
- propname = propnames[i]
- if propname == 'is retired':
- continue
- prop = properties[propname]
- if not isinstance(prop, hyperdb.Multilink):
- continue
- sv = getattr(row, propname)
- for entry in value:
- sv.append((int(entry),))
-
- self.db.dirty = 1
- creator = d.get('creator', 0)
- creation = d.get('creation', 0)
- self.db.addjournal(self.classname, str(newid), _CREATE, {}, creator,
- creation)
- return newid
-
# --- used by Database
def _commit(self):
''' called post commit of the DB.
tablename = "_%s.%s"%(self.classname, self.key)
return self.db._db.view("_%s" % tablename).ordered(1)
+ #
+ # import / export
+ #
+ def export_list(self, propnames, nodeid):
+ ''' Export a node - generate a list of CSV-able data in the order
+ specified by propnames for the given node.
+ '''
+ properties = self.getprops()
+ l = []
+ for prop in propnames:
+ proptype = properties[prop]
+ value = self.get(nodeid, prop)
+ # "marshal" data where needed
+ if value is None:
+ pass
+ elif isinstance(proptype, hyperdb.Date):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Interval):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Password):
+ value = str(value)
+ l.append(repr(value))
+
+ # append retired flag
+ l.append(repr(self.is_retired(nodeid)))
+
+ return l
+
+ def import_list(self, propnames, proplist):
+ ''' Import a node - all information including "id" is present and
+ should not be sanity checked. Triggers are not triggered. The
+ journal should be initialised using the "creator" and "creation"
+ information.
+
+ Return the nodeid of the node imported.
+ '''
+ if self.db.journaltag is None:
+ raise hyperdb.DatabaseError, 'Database open read-only'
+ properties = self.getprops()
+
+ d = {}
+ view = self.getview(READWRITE)
+ for i in range(len(propnames)):
+ value = eval(proplist[i])
+ if not value:
+ continue
+
+ propname = propnames[i]
+ if propname == 'id':
+ newid = value = int(value)
+ elif propname == 'is retired':
+ # is the item retired?
+ if int(value):
+ d['_isdel'] = 1
+ continue
+ elif value is None:
+ d[propname] = None
+ continue
+
+ prop = properties[propname]
+ if isinstance(prop, hyperdb.Date):
+ value = int(calendar.timegm(value))
+ elif isinstance(prop, hyperdb.Interval):
+ value = date.Interval(value).serialise()
+ elif isinstance(prop, hyperdb.Number):
+ value = int(value)
+ elif isinstance(prop, hyperdb.Boolean):
+ value = int(value)
+ elif isinstance(prop, hyperdb.Link) and value:
+ value = int(value)
+ elif isinstance(prop, hyperdb.Multilink):
+ # we handle multilinks separately
+ continue
+ d[propname] = value
+
+ # possibly make a new node
+ if not d.has_key('id'):
+ d['id'] = newid = self.maxid
+ self.maxid += 1
+
+ # save off the node
+ view.append(d)
+
+ # fix up multilinks
+ ndx = view.find(id=newid)
+ row = view[ndx]
+ for i in range(len(propnames)):
+ value = eval(proplist[i])
+ propname = propnames[i]
+ if propname == 'is retired':
+ continue
+ prop = properties[propname]
+ if not isinstance(prop, hyperdb.Multilink):
+ continue
+ sv = getattr(row, propname)
+ for entry in value:
+ sv.append((int(entry),))
+
+ self.db.dirty = 1
+ return newid
+
+ def export_journals(self):
+ '''Export a class's journal - generate a list of lists of
+ CSV-able data:
+
+ nodeid, date, user, action, params
+
+ No heading here - the columns are fixed.
+ '''
+ properties = self.getprops()
+ r = []
+ for nodeid in self.getnodeids():
+ for nodeid, date, user, action, params in self.history(nodeid):
+ date = date.get_tuple()
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ # make sure the params are eval()'able
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = value.get_tuple()
+ elif isinstance(prop, Interval):
+ value = value.get_tuple()
+ elif isinstance(prop, Password):
+ value = str(value)
+ params[propname] = value
+ l = [nodeid, date, user, action, params]
+ r.append(map(repr, l))
+ return r
+
+ def import_journals(self, entries):
+ '''Import a class's journal.
+
+ Uses setjournal() to set the journal for each item.'''
+ properties = self.getprops()
+ d = {}
+ for l in entries:
+ l = map(eval, l)
+ nodeid, date, user, action, params = l
+ r = d.setdefault(nodeid, [])
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = date.Date(value)
+ elif isinstance(prop, Interval):
+ value = date.Interval(value)
+ elif isinstance(prop, Password):
+ pwd = password.Password()
+ pwd.unpack(value)
+ value = pwd
+ params[propname] = value
+ r.append((nodeid, date.Date(date), user, action, params))
+
+ for nodeid, l in d.items():
+ self.db.setjournal(self.classname, nodeid, l)
+
def _fetchML(sv):
l = []
for row in sv:
index 684a04c8be8032af53e008f688a0a1227a7462f8..657b477cd4bdaa3f82f733ec537f9d2c1154b9c6 100644 (file)
-# $Id: rdbms_common.py,v 1.87 2004-03-31 07:25:14 richard Exp $
+# $Id: rdbms_common.py,v 1.88 2004-04-02 05:58:45 richard Exp $
''' Relational database (SQL) backend common code.
Basics:
self.arg, self.arg)
self.sql(sql, (entry, nodeid))
- # make sure we do the commit-time extra stuff for this node
- self.transactions.append((self.doSaveNode, (classname, nodeid, node)))
-
- def setnode(self, classname, nodeid, values, multilink_changes):
+ def setnode(self, classname, nodeid, values, multilink_changes={}):
''' Change the specified node.
'''
if __debug__:
print >>hyperdb.DEBUG, 'setnode', (self, sql, vals)
self.cursor.execute(sql, vals)
- # now the fun bit, updating the multilinks ;)
+ # we're probably coming from an import, not a change
+ if not multilink_changes:
+ for name in mls:
+ prop = props[name]
+ value = values[name]
+
+ t = '%s_%s'%(classname, name)
+
+ # clear out previous values for this node
+ # XXX numeric ids
+ self.sql('delete from %s where nodeid=%s'%(t, self.arg),
+ (nodeid,))
+
+ # insert the values for this node
+ for entry in values[name]:
+ sql = 'insert into %s (linkid, nodeid) values (%s,%s)'%(t,
+ self.arg, self.arg)
+ # XXX numeric ids
+ self.sql(sql, (entry, nodeid))
+
+ # we have multilink changes to apply
for col, (add, remove) in multilink_changes.items():
tn = '%s_%s'%(classname, col)
if add:
# XXX numeric ids
self.sql(sql, (int(nodeid), int(removeid)))
- # make sure we do the commit-time extra stuff for this node
- self.transactions.append((self.doSaveNode, (classname, nodeid, values)))
-
sql_to_hyperdb_value = {
hyperdb.String : str,
hyperdb.Date : lambda x:date.Date(str(x).replace(' ', '.')),
'link' or 'unlink' -- 'params' is (classname, nodeid, propname)
'retire' -- 'params' is None
'''
- # serialise the parameters now if necessary
- if isinstance(params, type({})):
- 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:
journaldate = date.Date()
# create the journal entry
- cols = ','.join('nodeid date tag action params'.split())
+ cols = 'nodeid,date,tag,action,params'
if __debug__:
print >>hyperdb.DEBUG, 'addjournal', (nodeid, journaldate,
self.save_journal(classname, cols, nodeid, journaldate,
journaltag, action, params)
+ def setjournal(self, classname, nodeid, journal):
+ '''Set the journal to the "journal" list.'''
+ # clear out any existing entries
+ self.sql('delete from %s__journal where nodeid=%s'%(classname,
+ self.arg), (nodeid,))
+
+ # create the journal entry
+ cols = 'nodeid,date,tag,action,params'
+
+ for nodeid, journaldate, journaltag, action, params in journal:
+ if __debug__:
+ print >>hyperdb.DEBUG, 'setjournal', (nodeid, journaldate,
+ journaltag, action, params)
+ self.save_journal(classname, cols, nodeid, journaldate,
+ journaltag, action, params)
+
def getjournal(self, classname, nodeid):
''' get the journal for id
'''
# clear the cache
self.clearCache()
- def doSaveNode(self, classname, nodeid, node):
- ''' dummy that just generates a reindex event
- '''
- # return the classname, nodeid so we reindex this content
- return (classname, nodeid)
-
def sql_close(self):
if __debug__:
print >>hyperdb.DEBUG, '+++ close database connection +++'
# XXX numeric ids
return str(newid)
- def export_list(self, propnames, nodeid):
- ''' Export a node - generate a list of CSV-able data in the order
- specified by propnames for the given node.
- '''
- properties = self.getprops()
- l = []
- for prop in propnames:
- proptype = properties[prop]
- value = self.get(nodeid, prop)
- # "marshal" data where needed
- if value is None:
- pass
- elif isinstance(proptype, hyperdb.Date):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Interval):
- value = value.get_tuple()
- elif isinstance(proptype, hyperdb.Password):
- value = str(value)
- l.append(repr(value))
- l.append(repr(self.is_retired(nodeid)))
- return l
-
- def import_list(self, propnames, proplist):
- ''' Import a node - all information including "id" is present and
- should not be sanity checked. Triggers are not triggered. The
- journal should be initialised using the "creator" and "created"
- information.
-
- Return the nodeid of the node imported.
- '''
- if self.db.journaltag is None:
- raise DatabaseError, 'Database open read-only'
- properties = self.getprops()
-
- # make the new node's property map
- d = {}
- retire = 0
- newid = None
- for i in range(len(propnames)):
- # Use eval to reverse the repr() used to output the CSV
- value = eval(proplist[i])
-
- # Figure the property for this column
- propname = propnames[i]
-
- # "unmarshal" where necessary
- if propname == 'id':
- newid = value
- continue
- elif propname == 'is retired':
- # is the item retired?
- if int(value):
- retire = 1
- continue
- elif value is None:
- d[propname] = None
- continue
-
- prop = properties[propname]
- if value is None:
- # don't set Nones
- continue
- elif isinstance(prop, hyperdb.Date):
- value = date.Date(value)
- elif isinstance(prop, hyperdb.Interval):
- value = date.Interval(value)
- elif isinstance(prop, hyperdb.Password):
- pwd = password.Password()
- pwd.unpack(value)
- value = pwd
- d[propname] = value
-
- # get a new id if necessary
- if newid is None:
- newid = self.db.newid(self.classname)
-
- # add the node and journal
- self.db.addnode(self.classname, newid, d)
-
- # retire?
- if retire:
- # use the arg for __retired__ to cope with any odd database type
- # conversion (hello, sqlite)
- sql = 'update _%s set __retired__=%s where id=%s'%(self.classname,
- self.db.arg, self.db.arg)
- if __debug__:
- print >>hyperdb.DEBUG, 'retire', (self, sql, newid)
- self.db.cursor.execute(sql, (1, newid))
-
- # 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']
- if d.has_key('actor'):
- del d['actor']
- self.db.addjournal(self.classname, newid, 'create', {}, creator,
- creation)
- return newid
-
_marker = []
def get(self, nodeid, propname, default=_marker, cache=1):
'''Get the value of a property on an existing node of this class.
for react in self.reactors[action]:
react(self.db, self, nodeid, oldvalues)
+ #
+ # import / export support
+ #
+ def export_list(self, propnames, nodeid):
+ ''' Export a node - generate a list of CSV-able data in the order
+ specified by propnames for the given node.
+ '''
+ properties = self.getprops()
+ l = []
+ for prop in propnames:
+ proptype = properties[prop]
+ value = self.get(nodeid, prop)
+ # "marshal" data where needed
+ if value is None:
+ pass
+ elif isinstance(proptype, hyperdb.Date):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Interval):
+ value = value.get_tuple()
+ elif isinstance(proptype, hyperdb.Password):
+ value = str(value)
+ l.append(repr(value))
+ l.append(repr(self.is_retired(nodeid)))
+ return l
+
+ def import_list(self, propnames, proplist):
+ ''' Import a node - all information including "id" is present and
+ should not be sanity checked. Triggers are not triggered. The
+ journal should be initialised using the "creator" and "created"
+ information.
+
+ Return the nodeid of the node imported.
+ '''
+ if self.db.journaltag is None:
+ raise DatabaseError, 'Database open read-only'
+ properties = self.getprops()
+
+ # make the new node's property map
+ d = {}
+ retire = 0
+ newid = None
+ for i in range(len(propnames)):
+ # Use eval to reverse the repr() used to output the CSV
+ value = eval(proplist[i])
+
+ # Figure the property for this column
+ propname = propnames[i]
+
+ # "unmarshal" where necessary
+ if propname == 'id':
+ newid = value
+ continue
+ elif propname == 'is retired':
+ # is the item retired?
+ if int(value):
+ retire = 1
+ continue
+ elif value is None:
+ d[propname] = None
+ continue
+
+ prop = properties[propname]
+ if value is None:
+ # don't set Nones
+ continue
+ elif isinstance(prop, hyperdb.Date):
+ value = date.Date(value)
+ elif isinstance(prop, hyperdb.Interval):
+ value = date.Interval(value)
+ elif isinstance(prop, hyperdb.Password):
+ pwd = password.Password()
+ pwd.unpack(value)
+ value = pwd
+ d[propname] = value
+
+ # get a new id if necessary
+ if newid is None or not self.hasnode(newid):
+ newid = self.db.newid(self.classname)
+ self.db.addnode(self.classname, newid, d)
+ else:
+ # update
+ self.db.setnode(self.classname, newid, d)
+
+ # retire?
+ if retire:
+ # use the arg for __retired__ to cope with any odd database type
+ # conversion (hello, sqlite)
+ sql = 'update _%s set __retired__=%s where id=%s'%(self.classname,
+ self.db.arg, self.db.arg)
+ if __debug__:
+ print >>hyperdb.DEBUG, 'retire', (self, sql, newid)
+ self.db.cursor.execute(sql, (1, newid))
+ return newid
+
+ def export_journals(self):
+ '''Export a class's journal - generate a list of lists of
+ CSV-able data:
+
+ nodeid, date, user, action, params
+
+ No heading here - the columns are fixed.
+ '''
+ properties = self.getprops()
+ r = []
+ for nodeid in self.getnodeids():
+ for nodeid, date, user, action, params in self.history(nodeid):
+ date = date.get_tuple()
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ # make sure the params are eval()'able
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = value.get_tuple()
+ elif isinstance(prop, Interval):
+ value = value.get_tuple()
+ elif isinstance(prop, Password):
+ value = str(value)
+ params[propname] = value
+ l = [nodeid, date, user, action, params]
+ r.append(map(repr, l))
+ return r
+
+ def import_journals(self, entries):
+ '''Import a class's journal.
+
+ Uses setjournal() to set the journal for each item.'''
+ properties = self.getprops()
+ d = {}
+ for l in entries:
+ l = map(eval, l)
+ nodeid, jdate, user, action, params = l
+ r = d.setdefault(nodeid, [])
+ if action == 'set':
+ for propname, value in params.items():
+ prop = properties[propname]
+ if value is None:
+ pass
+ elif isinstance(prop, Date):
+ value = date.Date(value)
+ elif isinstance(prop, Interval):
+ value = date.Interval(value)
+ elif isinstance(prop, Password):
+ pwd = password.Password()
+ pwd.unpack(value)
+ value = pwd
+ params[propname] = value
+ r.append((nodeid, date.Date(jdate), user, action, params))
+
+ for nodeid, l in d.items():
+ self.db.setjournal(self.classname, nodeid, l)
+
class FileClass(Class, hyperdb.FileClass):
'''This class defines a large chunk of data. To support this, it has a
mandatory String property "content" which is typically saved off