2007-06-11 Don Allingham <don@gramps-project.org>

* 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
This commit is contained in:
Don Allingham 2007-06-12 04:29:15 +00:00
parent 6bf09da13d
commit e05e6b4edd
9 changed files with 189 additions and 95 deletions

View File

@ -1,3 +1,11 @@
2007-06-11 Don Allingham <don@gramps-project.org>
* 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 <shura@gramps-project.org>
* src/DisplayState.py (DisplayState.__signals__): Port fixes from
2.2 tree.

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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),
]

View File

@ -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 \

View File

@ -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()

View File

@ -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

View File

@ -15702,6 +15702,18 @@ Very High</property>
<property name="focus_on_click">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="repair">
<property name="visible">True</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Repair</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
</widget>
</child>
</widget>
</child>
</widget>