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 # 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. 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 Instantiate the object. Note, this method should be overridden in
derived classes that properly set self.cursor and self.source derived classes that properly set self.cursor and self.source
""" """
self.cursor = self.source = None self.cursor = self.source = None
self.txn = txn
self._update = update
self.commit = commit
def __getattr__(self, name): def __getattr__(self, name):
""" """
@ -68,6 +72,8 @@ class GrampsCursor(object):
Context manager exit method Context manager exit method
""" """
self.close() self.close()
if self.txn and self.commit:
self.txn.commit()
return exc_type is None return exc_type is None
def __iter__(self): def __iter__(self):
@ -76,74 +82,38 @@ class GrampsCursor(object):
""" """
data = self.first() data = self.first()
_n = self.next # Saved attribute lookup in the loop
while data: while data:
yield data yield data
data = self.next() data = _n()
def first(self, *args, **kwargs): def _get(_flags=0):
""" Closure that returns a cursor get function """
def get(self, flags=0, **kwargs):
""" """
Return the first (index, data) pair in the database. Issue DBCursor get call (with DB_RMW flag if update requested)
Return results to caller
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.
If no data is available, None is returned.
""" """
data = self.cursor.get(
_flags | flags | (db.DB_RMW if self._update else 0),
**kwargs)
data = self.cursor.first(*args, **kwargs) return (data[0], loads(data[1])) if data else None
if data:
return (data[0], pickle.loads(data[1]))
return None
def next(self, *args, **kwargs): return get
# Use closure to define access methods
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 update(self, key, data, flags=0, **kwargs):
""" """
Return the next (index, data) pair in the database. Write the current key, data pair to 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.
""" """
self.cursor.put(key, dumps(data), flags=flags | db.DB_CURRENT,
data = self.cursor.next(*args, **kwargs) **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None
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.
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
def last(self, *args, **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.
"""
data = self.cursor.last(*args, **kwargs)
if data:
return (data[0], pickle.loads(data[1]))
return None

View File

@ -99,7 +99,8 @@ class GrampsDbBookmarks(object):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class GrampsDbReadCursor(GrampsCursor): 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.cursor = source.db.cursor(txn)
self.source = source self.source = source
@ -280,36 +281,36 @@ class GrampsDbRead(GrampsDbBase, Callback):
"""Return True when the file has a supported version.""" """Return True when the file has a supported version."""
return True return True
def __get_cursor(self, table): def get_cursor(self, table, *args, **kwargs):
try: try:
return GrampsDbReadCursor(table, self.txn) return GrampsDbReadCursor(table, self.txn)
except DBERRS, msg: except DBERRS, msg:
self.__log_error() self.__log_error()
raise Errors.DbError(msg) raise Errors.DbError(msg)
def get_person_cursor(self): def get_person_cursor(self, *args, **kwargs):
return self.__get_cursor(self.person_map) return self.get_cursor(self.person_map, *args, **kwargs)
def get_family_cursor(self): def get_family_cursor(self, *args, **kwargs):
return self.__get_cursor(self.family_map) return self.get_cursor(self.family_map, *args, **kwargs)
def get_event_cursor(self): def get_event_cursor(self, *args, **kwargs):
return self.__get_cursor(self.event_map) return self.get_cursor(self.event_map, *args, **kwargs)
def get_place_cursor(self): def get_place_cursor(self, *args, **kwargs):
return self.__get_cursor(self.place_map) return self.get_cursor(self.place_map, *args, **kwargs)
def get_source_cursor(self): def get_source_cursor(self, *args, **kwargs):
return self.__get_cursor(self.source_map) return self.get_cursor(self.source_map, *args, **kwargs)
def get_media_cursor(self): def get_media_cursor(self, *args, **kwargs):
return self.__get_cursor(self.media_map) return self.get_cursor(self.media_map, *args, **kwargs)
def get_repository_cursor(self): def get_repository_cursor(self, *args, **kwargs):
return self.__get_cursor(self.repository_map) return self.get_cursor(self.repository_map, *args, **kwargs)
def get_note_cursor(self): def get_note_cursor(self, *args, **kwargs):
return self.__get_cursor(self.note_map) return self.get_cursor(self.note_map, *args, **kwargs)
def load(self, name, callback, mode=DBMODE_R): 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.cursor = source.db.cursor(txn)
self.source = source self.source = source
@ -158,7 +159,8 @@ class GrampsDBDirCursor(GrampsCursor):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class GrampsDBDirAssocCursor(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.cursor = source.cursor(txn)
self.source = source self.source = source
@ -257,6 +259,17 @@ class GrampsDBDir(GrampsDbRead, Callback, UpdateCallback):
_log_error = __log_error _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 # cursors for lookups in the reference_map for back reference
# lookups. The reference_map has three indexes: # lookups. The reference_map has three indexes:
# the main index: a tuple of (primary_handle, referenced_handle) # the main index: a tuple of (primary_handle, referenced_handle)
@ -1802,11 +1815,9 @@ if __name__ == "__main__":
d.load(db_path, lambda x: x) d.load(db_path, lambda x: x)
print d.get_default_person() print d.get_default_person()
with d.get_person_cursor() as c: with d.get_person_cursor() as c:
for key, data in c: for key, data in c:
person = Person(data) person = Person(data)
print key, person.get_primary_name().get_name(), print key, person.get_primary_name().get_name(),
print d.surnames.keys() print d.surnames.keys()
print d.remove_from_surname_list.__doc__

View File

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