1. Enhance cursor.py to support updating records after reading them

2. Implement enhancement in write.py, get_cursor method
3. Exploit new capability in ChangeNames.py

svn: r13237
This commit is contained in:
Gerald Britton 2009-09-23 20:07:58 +00:00
parent dd10d9bfdb
commit 9b586eaf4f
4 changed files with 75 additions and 90 deletions

View File

@ -23,7 +23,8 @@
# Standard python modules
#
#-------------------------------------------------------------------------
import cPickle as pickle
from cPickle import dumps, loads
from bsddb import db
#-------------------------------------------------------------------------
#
@ -44,12 +45,15 @@ class GrampsCursor(object):
should be used.
"""
def __init__(self):
def __init__(self, txn=None, update=False, commit=False):
"""
Instantiate the object. Note, this method should be overridden in
derived classes that properly set self.cursor and self.source
"""
self.cursor = self.source = None
self.txn = txn
self._update = update
self.commit = commit
def __getattr__(self, name):
"""
@ -68,6 +72,8 @@ class GrampsCursor(object):
Context manager exit method
"""
self.close()
if self.txn and self.commit:
self.txn.commit()
return exc_type is None
def __iter__(self):
@ -76,74 +82,38 @@ class GrampsCursor(object):
"""
data = self.first()
_n = self.next # Saved attribute lookup in the loop
while data:
yield data
data = self.next()
data = _n()
def first(self, *args, **kwargs):
"""
Return the first (index, data) pair in the database.
This should be called before the first call to next(). Note that the
data return is in the format of the serialized format stored in the
database, not in the more usable class object. The data should be
converted to a class using the class's unserialize method.
def _get(_flags=0):
""" Closure that returns a cursor get function """
If no data is available, None is returned.
"""
data = self.cursor.first(*args, **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None
def get(self, flags=0, **kwargs):
"""
Issue DBCursor get call (with DB_RMW flag if update requested)
Return results to caller
"""
data = self.cursor.get(
_flags | flags | (db.DB_RMW if self._update else 0),
**kwargs)
def next(self, *args, **kwargs):
"""
Return the next (index, data) pair in the database.
Like the first() method, the data return is in the format of the
serialized format stored in the database, not in the more usable class
object. The data should be converted to a class using the class's
unserialize method.
return (data[0], loads(data[1])) if data else None
None is returned when no more data is available.
"""
data = self.cursor.next(*args, **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None
return get
def prev(self, *args, **kwargs):
"""
Return the previous (index, data) pair in the database.
Like the first() method, the data return is in the format of the
serialized format stored in the database, not in the more usable class
object. The data should be converted to a class using the class's
unserialize method.
# Use closure to define access methods
If no data is available, None is returned.
"""
data = self.cursor.prev(*args, **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None
current = _get(db.DB_CURRENT)
first = _get(db.DB_FIRST)
next = _get(db.DB_NEXT)
last = _get(db.DB_LAST)
prev = _get(db.DB_PREV)
def last(self, *args, **kwargs):
def update(self, key, data, flags=0, **kwargs):
"""
Return the last (index, data) pair in the database.
Like the first() method, the data return is in the format of the
serialized format stored in the database, not in the more usable class
object. The data should be converted to a class using the class's
unserialize method.
None is returned when no more data is available.
Write the current key, data pair to the database.
"""
data = self.cursor.last(*args, **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None
self.cursor.put(key, dumps(data), flags=flags | db.DB_CURRENT,
**kwargs)

View File

@ -99,7 +99,8 @@ class GrampsDbBookmarks(object):
#-------------------------------------------------------------------------
class GrampsDbReadCursor(GrampsCursor):
def __init__(self, source, txn=None):
def __init__(self, source, txn=None, **kwargs):
GrampsCursor.__init__(self, txn=txn, **kwargs)
self.cursor = source.db.cursor(txn)
self.source = source
@ -280,36 +281,36 @@ class GrampsDbRead(GrampsDbBase, Callback):
"""Return True when the file has a supported version."""
return True
def __get_cursor(self, table):
def get_cursor(self, table, *args, **kwargs):
try:
return GrampsDbReadCursor(table, self.txn)
except DBERRS, msg:
self.__log_error()
raise Errors.DbError(msg)
def get_person_cursor(self):
return self.__get_cursor(self.person_map)
def get_person_cursor(self, *args, **kwargs):
return self.get_cursor(self.person_map, *args, **kwargs)
def get_family_cursor(self):
return self.__get_cursor(self.family_map)
def get_family_cursor(self, *args, **kwargs):
return self.get_cursor(self.family_map, *args, **kwargs)
def get_event_cursor(self):
return self.__get_cursor(self.event_map)
def get_event_cursor(self, *args, **kwargs):
return self.get_cursor(self.event_map, *args, **kwargs)
def get_place_cursor(self):
return self.__get_cursor(self.place_map)
def get_place_cursor(self, *args, **kwargs):
return self.get_cursor(self.place_map, *args, **kwargs)
def get_source_cursor(self):
return self.__get_cursor(self.source_map)
def get_source_cursor(self, *args, **kwargs):
return self.get_cursor(self.source_map, *args, **kwargs)
def get_media_cursor(self):
return self.__get_cursor(self.media_map)
def get_media_cursor(self, *args, **kwargs):
return self.get_cursor(self.media_map, *args, **kwargs)
def get_repository_cursor(self):
return self.__get_cursor(self.repository_map)
def get_repository_cursor(self, *args, **kwargs):
return self.get_cursor(self.repository_map, *args, **kwargs)
def get_note_cursor(self):
return self.__get_cursor(self.note_map)
def get_note_cursor(self, *args, **kwargs):
return self.get_cursor(self.note_map, *args, **kwargs)
def load(self, name, callback, mode=DBMODE_R):
"""

View File

@ -142,12 +142,13 @@ def find_referenced_handle(key, data):
#-------------------------------------------------------------------------
#
# GrampsDBDirCursor
# GrampsWriteCursor
#
#-------------------------------------------------------------------------
class GrampsDBDirCursor(GrampsCursor):
class GrampsWriteCursor(GrampsCursor):
def __init__(self, source, txn=None):
def __init__(self, source, txn=None, **kwargs):
GrampsCursor.__init__(self, txn=txn, **kwargs)
self.cursor = source.db.cursor(txn)
self.source = source
@ -158,7 +159,8 @@ class GrampsDBDirCursor(GrampsCursor):
#-------------------------------------------------------------------------
class GrampsDBDirAssocCursor(GrampsCursor):
def __init__(self, source, txn=None):
def __init__(self, source, txn=None, **kwargs):
GrampsCursor.__init__(self, txn=txn, **kwargs)
self.cursor = source.cursor(txn)
self.source = source
@ -257,6 +259,17 @@ class GrampsDBDir(GrampsDbRead, Callback, UpdateCallback):
_log_error = __log_error
# Override get_cursor method from the superclass to add udpate
# capability
@catch_db_error
def get_cursor(self, table, txn=None, update=False, commit=False):
""" Helper function to return a cursor over a table """
if update and not txn:
txn = self.env.txn_begin(self.txn)
return GrampsWriteCursor(table, txn=txn or self.txn,
update=update, commit=commit)
# cursors for lookups in the reference_map for back reference
# lookups. The reference_map has three indexes:
# the main index: a tuple of (primary_handle, referenced_handle)
@ -1802,11 +1815,9 @@ if __name__ == "__main__":
d.load(db_path, lambda x: x)
print d.get_default_person()
with d.get_person_cursor() as c:
for key, data in c:
person = Person(data)
print key, person.get_primary_name().get_name(),
print d.surnames.keys()
print d.remove_from_surname_list.__doc__

View File

@ -30,6 +30,8 @@
#-------------------------------------------------------------------------
import gobject
import gtk
import cPickle
from bsddb.db import DB_CURRENT
#-------------------------------------------------------------------------
#
@ -40,6 +42,7 @@ import const
from gui.utils import ProgressMeter
import GrampsDisplay
import ManagedWindow
from gen.lib import Person
from QuestionDialog import OkDialog
from PluginUtils import Tool
@ -234,10 +237,10 @@ class ChangeNames(Tool.BatchTool, ManagedWindow.ManagedWindow):
for node in self.iter_list
if self.model.get_value(node,0)]
#for handle in self.db.get_person_handles(sort_handles=False):
for handle in self.db.get_person_handles(False):
with self.db.get_person_cursor(update=True, commit=True) as cursor:
for handle, data in cursor:
person = Person(data)
change = False
person = self.db.get_person_from_handle(handle)
for name in [person.get_primary_name()] + person.get_alternate_names():
sname = name.get_surname()
if sname in changelist:
@ -245,7 +248,7 @@ class ChangeNames(Tool.BatchTool, ManagedWindow.ManagedWindow):
sname = self.name_cap(sname)
name.set_surname(sname)
if change:
self.db.commit_person(person,self.trans)
cursor.update(handle, person.serialize())
self.db.transaction_commit(self.trans,_("Capitalization changes"))
self.db.enable_signals()