Add new backup preferences

Allow user to specify a backup directory.
Add option to backup on exit.
Add option to autobackup at regular intervals.
This commit is contained in:
Nick Hall 2016-12-06 22:44:38 +00:00
parent eceeddb1e4
commit 593f5a8f2b
8 changed files with 127 additions and 46 deletions

View File

@ -159,7 +159,9 @@ register('behavior.addons-url', "https://raw.githubusercontent.com/gramps-projec
register('database.backend', 'bsddb') register('database.backend', 'bsddb')
register('database.compress-backup', True) register('database.compress-backup', True)
register('database.autobackup', True) ## make backup when exiting, if there are changes register('database.backup-path', USER_HOME)
register('database.backup-on-exit', True)
register('database.autobackup', 0)
register('database.path', os.path.join(HOME_DIR, 'grampsdb')) register('database.path', os.path.join(HOME_DIR, 'grampsdb'))
register('export.proxy-order', register('export.proxy-order',

View File

@ -1995,23 +1995,3 @@ class DbWriteBase(DbReadBase):
person.birth_ref_index = birth_ref_index person.birth_ref_index = birth_ref_index
person.death_ref_index = death_ref_index person.death_ref_index = death_ref_index
def autobackup(self, user=None):
"""
Backup the current file as a backup file.
"""
from gramps.cli.user import User
if user is None:
user = User()
if self.is_open() and self.has_changed:
if user.uistate:
user.uistate.set_busy_cursor(True)
user.uistate.progress.show()
user.uistate.push_message(user.dbstate, _("Autobackup..."))
try:
self.backup(user=user)
except DbException as msg:
user.notify_error(_("Error saving backup data"), msg)
if user.uistate:
user.uistate.set_busy_cursor(False)
user.uistate.progress.hide()

View File

@ -646,8 +646,6 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
""" """
if self._directory: if self._directory:
if update: if update:
if config.get('database.autobackup'):
self.autobackup(user)
# This is just a dummy file to indicate last modified time of # This is just a dummy file to indicate last modified time of
# the database for gramps.cli.clidbman: # the database for gramps.cli.clidbman:
filename = os.path.join(self._directory, "meta_data.db") filename = os.path.join(self._directory, "meta_data.db")
@ -2576,20 +2574,6 @@ class DbGeneric(DbWriteBase, DbReadBase, UpdateCallback, Callback):
def redo(self, update_history=True): def redo(self, update_history=True):
return self.undodb.redo(update_history) return self.undodb.redo(update_history)
def backup(self, user=None):
"""
If you wish to support an optional backup routine, put it here.
"""
from gramps.plugins.export.exportxml import XmlWriter
from gramps.cli.user import User
if user is None:
user = User()
compress = config.get('database.compress-backup')
writer = XmlWriter(self, user, strip_photos=0, compress=compress)
timestamp = '{0:%Y-%m-%d-%H-%M-%S}'.format(datetime.datetime.now())
filename = os.path.join(self._directory, "backup-%s.gramps" % timestamp)
writer.write(filename)
def get_summary(self): def get_summary(self):
""" """
Returns dictionary of summary item. Returns dictionary of summary item.

View File

@ -1239,6 +1239,11 @@ class GrampsPreferences(ConfigureDialog):
def date_calendar_changed(self, obj): def date_calendar_changed(self, obj):
config.set('preferences.calendar-format-report', obj.get_active()) config.set('preferences.calendar-format-report', obj.get_active())
def autobackup_changed(self, obj):
active = obj.get_active()
config.set('database.autobackup', active)
self.uistate.set_autobackup_timer()
def add_date_panel(self, configdialog): def add_date_panel(self, configdialog):
grid = Gtk.Grid() grid = Gtk.Grid()
grid.set_border_width(12) grid.set_border_width(12)
@ -1464,6 +1469,33 @@ class GrampsPreferences(ConfigureDialog):
current_line, 'behavior.autoload') current_line, 'behavior.autoload')
current_line += 1 current_line += 1
self.backup_path_entry = Gtk.Entry()
self.add_path_box(grid,
_('Backup path'),
current_line, self.backup_path_entry,
config.get('database.backup-path'),
self.set_backup_path, self.select_backup_path)
current_line += 1
self.add_checkbox(grid,
_('Backup on exit'),
current_line, 'database.backup-on-exit')
current_line += 1
# Check for updates:
obox = Gtk.ComboBoxText()
formats = [_("Never"),
_("Every 15 minutes"),
_("Every 30 minutes"),
_("Every hour")]
list(map(obox.append_text, formats))
active = config.get('database.autobackup')
obox.set_active(active)
obox.connect('changed', self.autobackup_changed)
lwidget = BasicLabel("%s: " % _('Autobackup'))
grid.attach(lwidget, 1, current_line, 1, 1)
grid.attach(obox, 2, current_line, 1, 1)
return _('Family Tree'), grid return _('Family Tree'), grid
def __create_backend_combo(self): def __create_backend_combo(self):
@ -1543,6 +1575,31 @@ class GrampsPreferences(ConfigureDialog):
self.dbpath_entry.set_text(val) self.dbpath_entry.set_text(val)
f.destroy() f.destroy()
def set_backup_path(self, *obj):
path = self.backup_path_entry.get_text().strip()
config.set('database.backup-path', path)
def select_backup_path(self, *obj):
f = Gtk.FileChooserDialog(title=_("Select backup directory"),
parent=self.window,
action=Gtk.FileChooserAction.SELECT_FOLDER,
buttons=(_('_Cancel'),
Gtk.ResponseType.CANCEL,
_('_Apply'),
Gtk.ResponseType.OK)
)
backup_path = config.get('database.backup-path')
if not backup_path:
backup_path = config.get('database.path')
f.set_current_folder(os.path.dirname(backup_path))
status = f.run()
if status == Gtk.ResponseType.OK:
val = f.get_filename()
if val:
self.backup_path_entry.set_text(val)
f.destroy()
def update_idformat_entry(self, obj, constant): def update_idformat_entry(self, obj, constant):
config.set(constant, obj.get_text()) config.set(constant, obj.get_text())
self.dbstate.db.set_prefixes( self.dbstate.db.set_prefixes(

View File

@ -371,6 +371,7 @@ class DisplayState(Callback):
'nameformat-changed' : None, 'nameformat-changed' : None,
'grampletbar-close-changed' : None, 'grampletbar-close-changed' : None,
'update-available' : (list, ), 'update-available' : (list, ),
'autobackup' : None,
} }
#nav_type to message #nav_type to message
@ -409,6 +410,7 @@ class DisplayState(Callback):
self.disprel_active = None self.disprel_active = None
self.set_relationship_class() self.set_relationship_class()
self.export = False self.export = False
self.backup_timer = None
formatter = logging.Formatter('%(levelname)s %(name)s: %(message)s') formatter = logging.Formatter('%(levelname)s %(name)s: %(message)s')
warnbtn = status.get_warning_button() warnbtn = status.get_warning_button()
@ -421,6 +423,31 @@ class DisplayState(Callback):
# but this connection is still made! # but this connection is still made!
# self.dbstate.connect('database-changed', self.db_changed) # self.dbstate.connect('database-changed', self.db_changed)
def set_backup_timer(self):
"""
Set the backup timer.
"""
interval = config.get('database.autobackup')
if self.backup_timer is not None:
GLib.source_remove(self.backup_timer)
self.backup_timer = None
if interval == 1:
minutes = 15
elif interval == 2:
minutes = 30
elif interval == 3:
minutes = 60
if interval > 0:
self.backup_timer = GLib.timeout_add_seconds(
minutes*60, self.__emit_autobackup)
def __emit_autobackup(self):
"""
Emit an 'autobackup' signal.
"""
self.emit('autobackup')
return True
def screen_width(self): def screen_width(self):
""" """
Return the width of the current screen. Return the width of the current screen.

View File

@ -91,6 +91,7 @@ from .aboutdialog import GrampsAboutDialog
from .navigator import Navigator from .navigator import Navigator
from .views.tags import Tags from .views.tags import Tags
from .actiongroup import ActionGroup from .actiongroup import ActionGroup
from gramps.gen.db.exceptions import DbWriteFailure
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -311,6 +312,9 @@ class ViewManager(CLIManager):
# Need to call after plugins have been registered # Need to call after plugins have been registered
self.uistate.connect('update-available', self.process_updates) self.uistate.connect('update-available', self.process_updates)
self.check_for_updates() self.check_for_updates()
# Set autobackup
self.uistate.connect('autobackup', self.autobackup)
self.uistate.set_backup_timer()
def check_for_updates(self): def check_for_updates(self):
""" """
@ -750,7 +754,11 @@ class ViewManager(CLIManager):
# mark interface insenstitive to prevent unexpected events # mark interface insenstitive to prevent unexpected events
self.uistate.set_sensitive(False) self.uistate.set_sensitive(False)
# backup data, and close the database # backup data
if config.get('database.backup-on-exit'):
self.autobackup()
# close the database
if self.dbstate.is_open(): if self.dbstate.is_open():
self.dbstate.db.close(user=self.user) self.dbstate.db.close(user=self.user)
@ -1400,6 +1408,37 @@ class ViewManager(CLIManager):
self.uistate.push_message(self.dbstate, _("Backup aborted")) self.uistate.push_message(self.dbstate, _("Backup aborted"))
window.destroy() window.destroy()
def autobackup(self):
"""
Backup the current family tree.
"""
if self.dbstate.db.is_open() and self.dbstate.db.has_changed:
self.uistate.set_busy_cursor(True)
self.uistate.progress.show()
self.uistate.push_message(self.dbstate, _("Autobackup..."))
try:
self.__backup()
except DbWriteFailure as msg:
self.uistate.push_message(self.dbstate,
_("Error saving backup data"))
self.uistate.set_busy_cursor(False)
self.uistate.progress.hide()
def __backup(self):
"""
Backup database to a Gramps XML file.
"""
from gramps.plugins.export.exportxml import XmlWriter
backup_path = config.get('database.backup-path')
compress = config.get('database.compress-backup')
writer = XmlWriter(self.dbstate.db, self.user, strip_photos=0,
compress=compress)
timestamp = '{0:%Y-%m-%d-%H-%M-%S}'.format(datetime.datetime.now())
backup_name = "%s-%s.gramps" % (self.dbstate.db.get_dbname(),
timestamp)
filename = os.path.join(backup_path, backup_name)
writer.write(filename)
def select_backup_path(self, widget, path_entry): def select_backup_path(self, widget, path_entry):
""" """
Choose a backup folder. Make sure there is one highlighted in Choose a backup folder. Make sure there is one highlighted in

View File

@ -1507,7 +1507,6 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
""" """
if not self.db_is_open: if not self.db_is_open:
return return
self.autobackup(user)
if self.txn: if self.txn:
self.transaction_abort(self.transaction) self.transaction_abort(self.transaction)
self.env.txn_checkpoint() self.env.txn_checkpoint()

View File

@ -54,13 +54,6 @@ class InMemoryDB(DBAPI):
with open(versionpath, "w") as version_file: with open(versionpath, "w") as version_file:
version_file.write(str(self.VERSION)) version_file.write(str(self.VERSION))
def autobackup(self, user=None):
"""
Nothing to do, as we write it out anyway.
No backups for inmemory databases.
"""
pass
def load(self, directory, callback=None, mode=None, def load(self, directory, callback=None, mode=None,
force_schema_upgrade=False, force_schema_upgrade=False,
force_bsddb_upgrade=False, force_bsddb_upgrade=False,