Basics for back references now work, although doesn't update with edits yet

This commit is contained in:
Doug Blank 2015-05-23 14:52:43 -04:00
parent aa7928d35d
commit 56cf1b02ab

View File

@ -20,7 +20,8 @@ import gramps
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from gramps.gen.db import (DbReadBase, DbWriteBase, DbTxn,
KEY_TO_NAME_MAP, KEY_TO_CLASS_MAP)
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
@ -1245,6 +1246,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
return obj.handle
def commit_person(self, person, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if person.handle in self.person_map:
emit = "person-update"
@ -1271,6 +1273,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([person.handle],))
def commit_family(self, family, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if family.handle in self.family_map:
emit = "family-update"
@ -1292,6 +1295,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([family.handle],))
def commit_citation(self, citation, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if citation.handle in self.citation_map:
emit = "citation-update"
@ -1317,6 +1321,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([citation.handle],))
def commit_source(self, source, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if source.handle in self.source_map:
emit = "source-update"
@ -1342,6 +1347,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([source.handle],))
def commit_repository(self, repository, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if repository.handle in self.repository_map:
emit = "repository-update"
@ -1362,6 +1368,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([repository.handle],))
def commit_note(self, note, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if note.handle in self.note_map:
emit = "note-update"
@ -1382,6 +1389,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([note.handle],))
def commit_place(self, place, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if place.handle in self.place_map:
emit = "place-update"
@ -1407,6 +1415,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([place.handle],))
def commit_event(self, event, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if event.handle in self.event_map:
emit = "event-update"
@ -1429,6 +1438,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([event.handle],))
def commit_tag(self, tag, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if tag.handle in self.tag_map:
emit = "tag-update"
@ -1451,6 +1461,7 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.emit(emit, ([tag.handle],))
def commit_media_object(self, media, trans, change_time=None):
## FIXME: update reference, for back references
emit = None
if media.handle in self.media_map:
emit = "media-update"
@ -1675,64 +1686,6 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
[handle])
self.emit(KEY_TO_NAME_MAP[key] + "-delete", ([handle],))
def delete_primary_from_reference_map(self, handle, transaction, txn=None):
"""
Remove all references to the primary object from the reference_map.
handle should be utf-8
"""
primary_cur = self.get_reference_map_primary_cursor()
try:
ret = primary_cur.set(handle)
except:
ret = None
remove_list = set()
while (ret is not None):
(key, data) = ret
# data values are of the form:
# ((primary_object_class_name, primary_object_handle),
# (referenced_object_class_name, referenced_object_handle))
# so we need the second tuple give us a reference that we can
# combine with the primary_handle to get the main key.
main_key = (handle.decode('utf-8'), pickle.loads(data)[1][1])
# The trick is not to remove while inside the cursor,
# but collect them all and remove after the cursor is closed
remove_list.add(main_key)
ret = primary_cur.next_dup()
primary_cur.close()
# Now that the cursor is closed, we can remove things
for main_key in remove_list:
self.__remove_reference(main_key, transaction, txn)
def __remove_reference(self, key, transaction, txn):
"""
Remove the reference specified by the key, preserving the change in
the passed transaction.
"""
if isinstance(key, tuple):
#create a byte string key, first validity check in python 3!
for val in key:
if isinstance(val, bytes):
raise DbError(_('An attempt is made to save a reference key '
'which is partly bytecode, this is not allowed.\n'
'Key is %s') % str(key))
key = str(key)
if isinstance(key, str):
key = key.encode('utf-8')
if not self.readonly:
if not transaction.batch:
old_data = self.reference_map.get(key, txn=txn)
transaction.add(REFERENCE_KEY, TXNDEL, key, old_data, None)
#transaction.reference_del.append(str(key))
self.reference_map.delete(key, txn=txn)
## Missing:
def backup(self):
@ -1751,8 +1704,28 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.dbapi.close()
def find_backlink_handles(self, handle, include_classes=None):
## FIXME
return []
"""
Find all objects that hold a reference to the object handle.
Returns an interator over a list of (class_name, handle) tuples.
:param handle: handle of the object to search for.
:type handle: database handle
:param include_classes: list of class names to include in the results.
Default: None means include all classes.
:type include_classes: list of class names
Note that this is a generator function, it returns a iterator for
use in loops. If you want a list of the results use::
result_list = list(find_backlink_handles(handle))
"""
cur = self.dbapi.execute("SELECT * from reference WHERE ref_handle = ?;",
[handle])
rows = cur.fetchall()
for row in rows:
if (include_classes is None) or (row["obj_class"] in include_classes):
yield (row["obj_class"], row["obj_handle"])
def find_initial_person(self):
items = self.person_map.keys()
@ -2015,6 +1988,13 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
order_by TEXT ,
blob TEXT
);""")
# Reference
self.dbapi.execute("""CREATE TABLE IF NOT EXISTS reference (
obj_handle TEXT,
obj_class TEXT,
ref_handle TEXT,
ref_class TEXT
);""")
## Indices:
self.dbapi.execute("""CREATE INDEX IF NOT EXISTS
order_by ON person (order_by);
@ -2034,6 +2014,9 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
self.dbapi.execute("""CREATE INDEX IF NOT EXISTS
order_by ON tag (order_by);
""")
self.dbapi.execute("""CREATE INDEX IF NOT EXISTS
ref_handle ON reference (ref_handle);
""")
def redo(self, update_history=True):
## FIXME
@ -2111,13 +2094,43 @@ class DBAPI(DbWriteBase, DbReadBase, UpdateCallback, Callback):
name = None
return name
def reindex_reference_map(self):
## FIXME
pass
def reindex_reference_map(self, callback):
callback(4)
self.dbapi.execute("DELETE FROM reference;")
primary_table = (
(self.get_person_cursor, Person),
(self.get_family_cursor, Family),
(self.get_event_cursor, Event),
(self.get_place_cursor, Place),
(self.get_source_cursor, Source),
(self.get_citation_cursor, Citation),
(self.get_media_cursor, MediaObject),
(self.get_repository_cursor, Repository),
(self.get_note_cursor, Note),
(self.get_tag_cursor, Tag),
)
# Now we use the functions and classes defined above
# to loop through each of the primary object tables.
for cursor_func, class_func in primary_table:
logging.info("Rebuilding %s reference map" %
class_func.__name__)
with cursor_func() as cursor:
for found_handle, val in cursor:
obj = class_func.create(val)
references = set(obj.get_referenced_handles_recursively())
# handle addition of new references
for (ref_class_name, ref_handle) in references:
self.dbapi.execute("INSERT into reference VALUES(?, ?, ?, ?);",
[obj.handle,
obj.__class__.__name__,
ref_handle,
ref_class_name])
self.dbapi.commit()
callback(5)
def rebuild_secondary(self, update):
## FIXME
pass
self.reindex_reference_map(update)
def prepare_import(self):
"""