DB-API: added undo-redo infrastructure
This commit is contained in:
parent
d5c9c5114a
commit
1b89239323
@ -88,6 +88,7 @@ from .base import *
|
||||
from .dbconst import *
|
||||
from .txn import *
|
||||
from .exceptions import *
|
||||
from .undoredo import *
|
||||
|
||||
def find_surname_name(key, data):
|
||||
"""
|
||||
|
@ -1,4 +1,10 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import time
|
||||
import pickle
|
||||
from collections import deque
|
||||
|
||||
class DbUndo(object):
|
||||
@ -19,6 +25,20 @@ class DbUndo(object):
|
||||
self.redoq = deque()
|
||||
self.undo_history_timestamp = time.time()
|
||||
self.txn = None
|
||||
# N.B. the databases have to be in the same order as the numbers in
|
||||
# xxx_KEY in gen/db/dbconst.py
|
||||
self.mapbase = (
|
||||
self.db.person_map,
|
||||
self.db.family_map,
|
||||
self.db.source_map,
|
||||
self.db.event_map,
|
||||
self.db.media_map,
|
||||
self.db.place_map,
|
||||
self.db.repository_map,
|
||||
self.db.note_map,
|
||||
self.db.tag_map,
|
||||
self.db.citation_map,
|
||||
)
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
@ -85,6 +105,16 @@ class DbUndo(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __redo(self, update_history):
|
||||
"""
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __undo(self, update_history):
|
||||
"""
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def commit(self, txn, msg):
|
||||
"""
|
||||
Commit the transaction to the undo/redo database. "txn" should be
|
||||
@ -110,27 +140,40 @@ class DbUndo(object):
|
||||
return False
|
||||
return self.__redo(update_history)
|
||||
|
||||
def undoredo(func):
|
||||
def undo_reference(self, data, handle, db_map):
|
||||
"""
|
||||
Decorator function to wrap undo and redo operations within a bsddb
|
||||
transaction. It also catches bsddb errors and raises an exception
|
||||
as appropriate
|
||||
Helper method to undo a reference map entry
|
||||
"""
|
||||
pass
|
||||
try:
|
||||
if data is None:
|
||||
db_map.delete(handle, txn=self.txn)
|
||||
else:
|
||||
db_map.put(handle, data, txn=self.txn)
|
||||
|
||||
def __redo(self, update_history=True):
|
||||
"""
|
||||
Access the last undone transaction, and revert the data to the state
|
||||
before the transaction was undone.
|
||||
"""
|
||||
pass
|
||||
except DBERRS as msg:
|
||||
self.db._log_error()
|
||||
raise DbError(msg)
|
||||
|
||||
def __undo(self, db=None, update_history=True):
|
||||
def undo_data(self, data, handle, db_map, emit, signal_root):
|
||||
"""
|
||||
Access the last committed transaction, and revert the data to the
|
||||
state before the transaction was committed.
|
||||
Helper method to undo/redo the changes made
|
||||
"""
|
||||
pass
|
||||
try:
|
||||
if data is None:
|
||||
emit(signal_root + '-delete', ([handle2internal(handle)],))
|
||||
db_map.delete(handle, txn=self.txn)
|
||||
else:
|
||||
ex_data = db_map.get(handle, txn=self.txn)
|
||||
if ex_data:
|
||||
signal = signal_root + '-update'
|
||||
else:
|
||||
signal = signal_root + '-add'
|
||||
db_map.put(handle, data, txn=self.txn)
|
||||
emit(signal, ([handle2internal(handle)],))
|
||||
|
||||
except DBERRS as msg:
|
||||
self.db._log_error()
|
||||
raise DbError(msg)
|
||||
|
||||
undo_count = property(lambda self:len(self.undoq))
|
||||
redo_count = property(lambda self:len(self.redoq))
|
||||
|
@ -19,12 +19,11 @@ import shutil
|
||||
import gramps
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.gettext
|
||||
from gramps.gen.db import (DbReadBase, DbWriteBase, DbTxn,
|
||||
from gramps.gen.db import (DbReadBase, DbWriteBase, DbTxn, DbUndo,
|
||||
KEY_TO_NAME_MAP, KEY_TO_CLASS_MAP,
|
||||
CLASS_TO_KEY_MAP)
|
||||
from gramps.gen.utils.callback import Callback
|
||||
from gramps.gen.updatecallback import UpdateCallback
|
||||
from gramps.gen.db.undoredo import DbUndo
|
||||
from gramps.gen.db.dbconst import *
|
||||
from gramps.gen.db import (PERSON_KEY,
|
||||
FAMILY_KEY,
|
||||
@ -52,6 +51,71 @@ def touch(fname, mode=0o666, dir_fd=None, **kwargs):
|
||||
os.utime(f.fileno() if os.utime in os.supports_fd else fname,
|
||||
dir_fd=None if os.supports_fd else dir_fd, **kwargs)
|
||||
|
||||
class DBAPIUndo(DbUndo):
|
||||
def __init__(self, grampsdb, path):
|
||||
super(DBAPIUndo, self).__init__(grampsdb)
|
||||
self.undodb = grampsdb
|
||||
self.path = path
|
||||
|
||||
def open(self, value=None):
|
||||
"""
|
||||
Open the backing storage. Needs to be overridden in the derived
|
||||
class.
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the backing storage. Needs to be overridden in the derived
|
||||
class.
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
def append(self, value):
|
||||
"""
|
||||
Add a new entry on the end. Needs to be overridden in the derived
|
||||
class.
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""
|
||||
Returns an entry by index number. Needs to be overridden in the
|
||||
derived class.
|
||||
"""
|
||||
return None
|
||||
# FIXME
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
"""
|
||||
Set an entry to a value. Needs to be overridden in the derived class.
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
Returns the number of entries. Needs to be overridden in the derived
|
||||
class.
|
||||
"""
|
||||
return 0
|
||||
# FIXME
|
||||
|
||||
def __redo(self, update_history):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
def __undo(self, update_history):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
# FIXME
|
||||
|
||||
class Environment(object):
|
||||
"""
|
||||
Implements the Environment API.
|
||||
@ -427,6 +491,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
self.set_repository_id_prefix('R%04d')
|
||||
self.set_note_id_prefix('N%04d')
|
||||
# ----------------------------------
|
||||
self.undodb = None
|
||||
self.id_trans = DBAPITxn("ID Transaction", self)
|
||||
self.fid_trans = DBAPITxn("FID Transaction", self)
|
||||
self.pid_trans = DBAPITxn("PID Transaction", self)
|
||||
@ -484,19 +549,12 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
contains_func="has_gramps_id_func")
|
||||
self.tag_map = Map(Table(self._tables["Tag"]))
|
||||
self.metadata = Map(Table({"cursor_func": lambda: MetaCursor()}))
|
||||
## FIXME: add appropriate methods:
|
||||
self.name_group = Map(Table({"handles_func": self.get_name_group_keys, # keys
|
||||
"has_handle_func": self.has_name_group_key, # key in table
|
||||
"cursor_func": None, # create a cursor, values
|
||||
"add_func": self.set_name_group_mapping, # add a key, value
|
||||
"count_func": None})) # how many items?
|
||||
self.undo_callback = None
|
||||
self.redo_callback = None
|
||||
self.undo_history_callback = None
|
||||
self.modified = 0
|
||||
self.txn = DBAPITxn("DBAPI Transaction", self)
|
||||
self.transaction = None
|
||||
self.undodb = DbUndo(self)
|
||||
self.abort_possible = False
|
||||
self._bm_changes = 0
|
||||
self._directory = directory
|
||||
@ -984,7 +1042,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
return (handle for handle in self.family_map.keys())
|
||||
|
||||
def get_tag_from_name(self, name):
|
||||
## Slow, but typically not too many tags:
|
||||
## FIXME: Slow, but typically not too many tags:
|
||||
for data in self.tag_map.values():
|
||||
tag = Tag.create(data)
|
||||
if tag.name == name:
|
||||
@ -2244,6 +2302,11 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
|
||||
# surname list
|
||||
self.surname_list = self.get_metadata('surname_list')
|
||||
|
||||
self._directory = directory
|
||||
self.undolog = os.path.join(self._directory, DBUNDOFN)
|
||||
self.undodb = DBAPIUndo(self, self.undolog)
|
||||
self.undodb.open()
|
||||
|
||||
def set_prefixes(self, person, media, family, source, citation,
|
||||
place, event, repository, note):
|
||||
@ -2576,22 +2639,22 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
|
||||
return glocale.sort_key(tag.get_name())
|
||||
|
||||
def backup(self):
|
||||
## FIXME
|
||||
"""
|
||||
If you wish to support an optional backup routine, put it here.
|
||||
"""
|
||||
pass
|
||||
|
||||
def restore(self):
|
||||
## FIXME
|
||||
"""
|
||||
If you wish to support an optional restore routine, put it here.
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_undodb(self):
|
||||
## FIXME
|
||||
return None
|
||||
return self.undodb
|
||||
|
||||
def undo(self, update_history=True):
|
||||
## FIXME
|
||||
pass
|
||||
return self.undodb.undo(update_history)
|
||||
|
||||
def redo(self, update_history=True):
|
||||
## FIXME
|
||||
pass
|
||||
|
||||
return self.undodb.redo(update_history)
|
||||
|
Loading…
Reference in New Issue
Block a user