diff --git a/src/gen/db/cursor.py b/src/gen/db/cursor.py index 4e1502469..970868a4f 100644 --- a/src/gen/db/cursor.py +++ b/src/gen/db/cursor.py @@ -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) diff --git a/src/gen/db/read.py b/src/gen/db/read.py index a3cfa37bc..1796251bb 100644 --- a/src/gen/db/read.py +++ b/src/gen/db/read.py @@ -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): """ diff --git a/src/gen/db/write.py b/src/gen/db/write.py index 4731f6c62..2c2dfca45 100644 --- a/src/gen/db/write.py +++ b/src/gen/db/write.py @@ -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__ diff --git a/src/plugins/tool/ChangeNames.py b/src/plugins/tool/ChangeNames.py index 05d40516b..22d5a5c71 100644 --- a/src/plugins/tool/ChangeNames.py +++ b/src/plugins/tool/ChangeNames.py @@ -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()