From e05e6b4eddb591a5c5d681bb876ee3b34087a02f Mon Sep 17 00:00:00 2001 From: Don Allingham Date: Tue, 12 Jun 2007 04:29:15 +0000 Subject: [PATCH] 2007-06-11 Don Allingham * src/ViewManager.py: Improve backup strategy * src/GrampsDb/_GrampsDBDir.py: Improve backup strategy * src/DbManager.py: Improve backup strategy * src/glade/gramps.glade: Improve backup strategy * src/Errors.py: Improve backup strategy * src/GrampsDbUtils/_Backup.py: Improve backup strategy svn: r8538 --- ChangeLog | 8 +++ src/DbManager.py | 30 +++++++++ src/Errors.py | 12 ++++ src/GrampsDb/_GrampsDBDir.py | 92 ++++++++++++++------------ src/GrampsDbUtils/_Backup.py | 121 +++++++++++++++++++++-------------- src/Makefile.am | 2 +- src/ViewManager.py | 3 +- src/docgen/GtkPrint.py | 4 +- src/glade/gramps.glade | 12 ++++ 9 files changed, 189 insertions(+), 95 deletions(-) diff --git a/ChangeLog b/ChangeLog index b5e88cde7..a43057bc4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-06-11 Don Allingham + * src/ViewManager.py: Improve backup strategy + * src/GrampsDb/_GrampsDBDir.py: Improve backup strategy + * src/DbManager.py: Improve backup strategy + * src/glade/gramps.glade: Improve backup strategy + * src/Errors.py: Improve backup strategy + * src/GrampsDbUtils/_Backup.py: Improve backup strategy + 2007-06-06 Alex Roitman * src/DisplayState.py (DisplayState.__signals__): Port fixes from 2.2 tree. diff --git a/src/DbManager.py b/src/DbManager.py index dbc064252..a88c6438c 100644 --- a/src/DbManager.py +++ b/src/DbManager.py @@ -58,6 +58,9 @@ import gtk.glade # #------------------------------------------------------------------------- import QuestionDialog +import GrampsDb +import GrampsDbUtils +import Config #------------------------------------------------------------------------- # @@ -98,6 +101,7 @@ class DbManager: self.remove = self.glade.get_widget('remove') self.dblist = self.glade.get_widget('dblist') self.rename = self.glade.get_widget('rename') + self.repair = self.glade.get_widget('repair') self.model = None self.dbstate = dbstate self.column = None @@ -123,6 +127,7 @@ class DbManager: self.remove.connect('clicked', self.remove_db) self.new.connect('clicked', self.new_db) self.rename.connect('clicked', self.rename_db) + self.repair.connect('clicked', self.repair_db) self.selection.connect('changed', self.selection_changed) self.dblist.connect('button-press-event', self.button_press) @@ -155,6 +160,7 @@ class DbManager: if not node: self.connect.set_sensitive(False) self.rename.set_sensitive(False) + self.repair.set_sensitive(False) self.remove.set_sensitive(False) else: if store.get_value(node, OPEN_COL): @@ -162,6 +168,7 @@ class DbManager: else: self.connect.set_sensitive(True) self.rename.set_sensitive(True) + self.repair.set_sensitive(True) self.remove.set_sensitive(True) def build_interface(self): @@ -320,6 +327,29 @@ class DbManager: self.dblist.set_cursor(path, focus_column=self.column, start_editing=True) + def repair_db(self, obj): + """ + Start the rename process by calling the start_editing option on + the line with the cursor. + """ + store, node = self.selection.get_selected() + dirname = store[node][1] + opened = store[node][5] + if opened: + self.dbstate.no_database() + + # delete files that are not backup files or the .txt file + for filename in os.listdir(dirname): + if os.path.splitext(filename)[1] not in (".gbkp", ".txt"): + os.unlink(os.path.join(dirname,filename)) + + dbclass = GrampsDb.gramps_db_factory(db_type = "x-directory/normal") + db = dbclass(Config.get(Config.TRANSACTIONS)) + db.set_save_path(dirname) + db.load(dirname, None) + GrampsDbUtils.Backup.restore(db) + db.close() + def new_db(self, obj): """ Callback wrapper around the actual routine that creates the diff --git a/src/Errors.py b/src/Errors.py index c0e31899f..5b9d5a4b6 100644 --- a/src/Errors.py +++ b/src/Errors.py @@ -139,3 +139,15 @@ class MaskError(Exception): class ValidationError(Exception): pass +class DbError(Exception): + """Error used to report that the request window is already displayed.""" + def __init__(self, value): + Exception.__init__(self) + if type(value) == tuple: + self.value = value[1] + else: + self.value = value + + def __str__(self): + "Return string representation" + return self.value diff --git a/src/GrampsDb/_GrampsDBDir.py b/src/GrampsDb/_GrampsDBDir.py index 40b30311c..c3d11f5f8 100644 --- a/src/GrampsDb/_GrampsDBDir.py +++ b/src/GrampsDb/_GrampsDBDir.py @@ -34,6 +34,7 @@ import os import shutil import re import time + from gettext import gettext as _ from bsddb import dbshelve, db import logging @@ -161,7 +162,6 @@ class GrampsDBDirDupCursor(GrampsDBDirAssocCursor): def next_dup(self): return self.cursor.next_dup() - #------------------------------------------------------------------------- # # GrampsDBDir @@ -179,13 +179,13 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): self.secondary_connected = False self.UseTXN = use_txn - def open_flags(self): + def __open_flags(self): if self.UseTXN: return db.DB_CREATE | db.DB_AUTO_COMMIT else: return db.DB_CREATE - def open_table(self, file_name, table_name, dbtype=db.DB_HASH): + def __open_table(self, file_name, table_name, dbtype=db.DB_HASH): dbmap = dbshelve.DBShelf(self.env) dbmap.db.set_pagesize(16384) @@ -194,7 +194,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if self.readonly: dbmap.open(fname, table_name, dbtype, db.DB_RDONLY) else: - dbmap.open(fname, table_name, dbtype, self.open_flags(), 0666) + dbmap.open(fname, table_name, dbtype, self.__open_flags(), 0666) return dbmap def _all_handles(self,table): @@ -367,7 +367,8 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): dbversion = self.metadata.get('version',default=0) return not self.readonly and dbversion < _DBVERSION - def load(self, name, callback,mode="w"): + def load(self, name, callback, mode="w"): + if self.db_is_open: self.close() @@ -375,7 +376,8 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if self.readonly: self.UseTXN = False - callback(12) + if callback: + callback(12) self.full_name = os.path.abspath(name) self.brief_name = os.path.basename(name) @@ -404,31 +406,33 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): env_flags = db.DB_CREATE | db.DB_PRIVATE | db.DB_INIT_MPOOL env_name = os.path.expanduser('~') - self.env.open(env_name,env_flags) + self.env.open(env_name, env_flags) if self.UseTXN: self.env.txn_checkpoint() - callback(25) - self.metadata = self.open_table(self.full_name, META) + if callback: + callback(25) + self.metadata = self.__open_table(self.full_name, META) # If we cannot work with this DB version, # it makes no sense to go further if not self.version_supported: self._close_early() - self.family_map = self.open_table(self.full_name, FAMILY_TBL) - self.place_map = self.open_table(self.full_name, PLACES_TBL) - self.source_map = self.open_table(self.full_name, SOURCES_TBL) - self.media_map = self.open_table(self.full_name, MEDIA_TBL) - self.event_map = self.open_table(self.full_name, EVENTS_TBL) - self.person_map = self.open_table(self.full_name, PERSON_TBL) - self.repository_map = self.open_table(self.full_name, REPO_TBL) - self.note_map = self.open_table(self.full_name, NOTE_TBL) - self.reference_map = self.open_table(self.full_name, REF_MAP, + self.family_map = self.__open_table(self.full_name, FAMILY_TBL) + self.place_map = self.__open_table(self.full_name, PLACES_TBL) + self.source_map = self.__open_table(self.full_name, SOURCES_TBL) + self.media_map = self.__open_table(self.full_name, MEDIA_TBL) + self.event_map = self.__open_table(self.full_name, EVENTS_TBL) + self.person_map = self.__open_table(self.full_name, PERSON_TBL) + self.repository_map = self.__open_table(self.full_name, REPO_TBL) + self.note_map = self.__open_table(self.full_name, NOTE_TBL) + self.reference_map = self.__open_table(self.full_name, REF_MAP, dbtype=db.DB_BTREE) - callback(37) + if callback: + callback(37) - self._load_metadata() + self.__load_metadata() gstats = self.metadata.get('gender_stats', default=None) @@ -462,17 +466,20 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if self.need_upgrade(): self.gramps_upgrade(callback) - callback(50) + if callback: + callback(50) if not self.secondary_connected: - self.connect_secondary() + self.__connect_secondary() - callback(75) + if callback: + callback(75) self.open_undodb() self.db_is_open = True - callback(87) + if callback: + callback(87) # Re-set the undo history to a fresh session start self.undoindex = -1 @@ -487,7 +494,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): db_copy(other_database,self,callback) return 1 - def _load_metadata(self): + def __load_metadata(self): # name display formats self.name_formats = self.metadata.get('name_formats', default=[]) # upgrade formats if they were saved in the old way @@ -547,7 +554,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): # surname list self.surname_list = self.metadata.get('surname_list', default=[]) - def connect_secondary(self): + def __connect_secondary(self): """ This method connects or creates secondary index tables. It assumes that the tables either exist and are in the right @@ -561,7 +568,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if self.readonly: table_flags = db.DB_RDONLY else: - table_flags = self.open_flags() + table_flags = self.__open_flags() self.surnames = db.DB(self.env) self.surnames.set_flags(db.DB_DUP | db.DB_DUPSORT) @@ -653,11 +660,11 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): self.rmap_index = len(self.repository_map) self.nmap_index = len(self.note_map) - def rebuild_secondary(self,callback): + def rebuild_secondary(self,callback=None): if self.readonly: return - table_flags = self.open_flags() + table_flags = self.__open_flags() # remove existing secondary indices @@ -680,16 +687,19 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): db.close() env = db.DB(self.env) env.remove(_mkname(self.full_name, name), name) - callback(index) + if callback: + callback(index) index += 1 - callback(11) + if callback: + callback(11) # Set flag saying that we have removed secondary indices # and then call the creating routine self.secondary_connected = False - self.connect_secondary() - callback(12) + self.__connect_secondary() + if callback: + callback(12) def find_backlink_handles(self, handle, include_classes=None): """ @@ -744,7 +754,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): return - def _delete_primary_from_reference_map(self,handle,transaction,txn=None): + def __delete_primary_from_reference_map(self,handle,transaction,txn=None): """ Remove all references to the primary object from the reference_map. """ @@ -915,10 +925,10 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): callback(3) # Open reference_map and primapry map - self.reference_map = self.open_table( + self.reference_map = self.__open_table( _mkname(self.full_name, REF_MAP), REF_MAP, dbtype=db.DB_BTREE) - open_flags = self.open_flags() + open_flags = self.__open_flags() self.reference_map_primary_map = db.DB(self.env) self.reference_map_primary_map.set_flags(db.DB_DUP) self.reference_map_primary_map.open( @@ -1146,7 +1156,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): self.metadata = None self.db_is_open = False - def _do_remove_object(self,handle,transaction,data_map,key,del_list): + def __do_remove_object(self,handle,transaction,data_map,key,del_list): if self.readonly or not handle: return @@ -1156,7 +1166,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): the_txn = self.env.txn_begin() else: the_txn = None - self._delete_primary_from_reference_map(handle,transaction, + self.__delete_primary_from_reference_map(handle,transaction, txn=the_txn) data_map.delete(handle,txn=the_txn) if not self.UseTXN: @@ -1164,7 +1174,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if the_txn: the_txn.commit() else: - self._delete_primary_from_reference_map(handle,transaction) + self.__delete_primary_from_reference_map(handle,transaction) old_data = data_map.get(handle,txn=self.txn) transaction.add(key,handle,old_data,None) del_list.append(handle) @@ -1364,7 +1374,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): add_list.append((handle,new_data)) return old_data - def _do_commit(self, add_list, db_map): + def __do_commit(self, add_list, db_map): retlist = [] for (handle, data) in add_list: db_map.put(handle, data, self.txn) @@ -1461,7 +1471,7 @@ class GrampsDBDir(GrampsDbBase,UpdateCallback): if not transaction.no_magic: # create new secondary indices to replace the ones removed - open_flags = self.open_flags() + open_flags = self.__open_flags() dupe_flags = db.DB_DUP|db.DB_DUPSORT self.surnames = db.DB(self.env) diff --git a/src/GrampsDbUtils/_Backup.py b/src/GrampsDbUtils/_Backup.py index 473087bbb..4fb179673 100644 --- a/src/GrampsDbUtils/_Backup.py +++ b/src/GrampsDbUtils/_Backup.py @@ -1,7 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2007 Donald N. Allingham # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,11 +18,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: _WriteXML.py 8144 2007-02-17 22:12:56Z hippy $ - """ -Contains the interface to allow a database to get written using -GRAMPS' XML file format. +Provides backup and restore functions for a database """ #------------------------------------------------------------------------- @@ -46,72 +43,98 @@ from QuestionDialog import ErrorDialog #------------------------------------------------------------------------ import logging import os +from GrampsDb import _GrampsDBDir as GrampsDBDir import cPickle as pickle -LOG = logging.getLogger(".Backukp") +LOG = logging.getLogger(".Backup") def export(database): + """ + Exports the database to a set of backup files. These files consist + of the pickled database tables, one file for each table. + + The heavy lifting is done by the private __do__export function. The + purpose of this function is to catch any exceptions that occur. + + @param database: database instance to backup + @type database: GrampsDbDir + """ try: - do_export(database) + __do_export(database) except (OSError, IOError), msg: - ErrorDialog( - _("Error saving backup data"), - str(msg)) + ErrorDialog(_("Error saving backup data"), str(msg)) -def do_export(database): +def __do_export(database): + """ + Loop through each table of the database, saving the pickled data + a file. - tables = [ - ('person', database.person_map.db), - ('family', database.family_map.db), - ('place', database.place_map.db), - ('source', database.source_map.db), - ('repo', database.repository_map.db), - ('note', database.note_map.db), - ('media', database.media_map.db), - ('event', database.event_map.db), - ('meta_data', database.metadata.db), - ] - - for (base, db) in tables: + @param database: database instance to backup + @type database: GrampsDbDir + """ + for (base, tbl) in __build_tbl_map(database): backup_name = os.path.join(database.get_save_path(), base + ".gbkp") - backup_table = open(backup_name, 'w') + backup_table = open(backup_name, 'wb') - cursor = db.cursor() - d = cursor.first() - while d: - pickle.dump(d[1], backup_table, 2) - d = cursor.next() + cursor = tbl.cursor() + data = cursor.first() + while data: + pickle.dump(data, backup_table, 2) + data = cursor.next() cursor.close() backup_table.close() def restore(database): + """ + Restores the database to a set of backup files. These files consist + of the pickled database tables, one file for each table. + + The heavy lifting is done by the private __do__restore function. The + purpose of this function is to catch any exceptions that occur. + + @param database: database instance to restore + @type database: GrampsDbDir + """ try: - do_restore(database) + __do_restore(database) except (OSError, IOError), msg: - ErrorDialog( - _("Error restoring backup data"), - str(msg)) + ErrorDialog(_("Error restoring backup data"), str(msg)) -def do_restore(database): +def __do_restore(database): + """ + Loop through each table of the database, restoring the pickled data + to the appropriate database file. - tables = [ - ('person', database.person_map), - ('family', database.family_map), - ('place', database.place_map), - ('source', database.place_map), - ('repo', database.repository_map), - ('note', database.note_map), - ('media', database.media_map), - ('event', database.media_map), - ] - - for (base, db) in tables: + @param database: database instance to backup + @type database: GrampsDbDir + """ + for (base, tbl) in __build_tbl_map(database): backup_name = os.path.join(database.get_save_path(), base + ".gbkp") - backup_table = open(backup_name, 'r') + backup_table = open(backup_name, 'rb') try: while True: - db[data[0]] = pickle.load(backup_table) + data = pickle.load(backup_table) + tbl[data[0]] = data[1] except EOFError: backup_table.close() + database.rebuild_secondary() +def __build_tbl_map(database): + """ + Builds a table map of names to database tables. + + @param database: database instance to backup + @type database: GrampsDbDir + """ + return [ + ( GrampsDBDir.PERSON_TBL, database.person_map.db), + ( GrampsDBDir.FAMILY_TBL, database.family_map.db), + ( GrampsDBDir.PLACES_TBL, database.place_map.db), + ( GrampsDBDir.SOURCES_TBL, database.source_map.db), + ( GrampsDBDir.REPO_TBL, database.repository_map.db), + ( GrampsDBDir.NOTE_TBL, database.note_map.db), + ( GrampsDBDir.MEDIA_TBL, database.media_map.db), + ( GrampsDBDir.EVENTS_TBL, database.event_map.db), + ( GrampsDBDir.META, database.metadata.db), + ] diff --git a/src/Makefile.am b/src/Makefile.am index 097b3f0d4..1c3d0e8f7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -84,7 +84,7 @@ gdir_PYTHON = \ MOSTLYCLEANFILES = *pyc *pyo # Which modules to document -docmodules = RelLib DateHandler GrampsDb Simple #Filters ReportBase GrampsDbUtils +docmodules = RelLib DateHandler GrampsDb Simple BaseDoc #Filters ReportBase GrampsDbUtils pycheck: for d in $(SUBDIRS) ; do \ diff --git a/src/ViewManager.py b/src/ViewManager.py index 6d8ad82f8..7b06bdca0 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -528,7 +528,7 @@ class ViewManager: def backup(self): """ - Backup the current file as an XML file. + Backup the current file as a backup file. """ import GrampsDbUtils @@ -959,7 +959,6 @@ class ViewManager: "\n" + str(msg)) return - self.state.change_database(dbclass(Config.get(Config.TRANSACTIONS))) self.state.db.disable_signals() diff --git a/src/docgen/GtkPrint.py b/src/docgen/GtkPrint.py index b6d9a235c..a274a6eea 100644 --- a/src/docgen/GtkPrint.py +++ b/src/docgen/GtkPrint.py @@ -122,7 +122,8 @@ class PreviewWindow(gtk.Window): print_context, parent): gtk.Window.__init__(self) - + self.set_default_size(640, 480) + self._operation = operation self._preview_operation = preview_operation @@ -318,7 +319,6 @@ class CairoJob(object): y = 20 x = 30 - print "self._doc: ", text="\n".join(self._doc) # Draw some text diff --git a/src/glade/gramps.glade b/src/glade/gramps.glade index f283f8e94..a11c6cfad 100644 --- a/src/glade/gramps.glade +++ b/src/glade/gramps.glade @@ -15702,6 +15702,18 @@ Very High True + + + + True + True + True + Repair + True + GTK_RELIEF_NORMAL + True + +