Check&Repair; add checks for 'backlinks' and repair if needed.
This commit is contained in:
@@ -61,7 +61,7 @@ ngettext = glocale.translation.ngettext # else "nearby" comments are ignored
|
|||||||
from gramps.gen.lib import (Citation, Event, EventType, Family, Media,
|
from gramps.gen.lib import (Citation, Event, EventType, Family, Media,
|
||||||
Name, Note, Person, Place, Repository, Source,
|
Name, Note, Person, Place, Repository, Source,
|
||||||
StyledText, Tag)
|
StyledText, Tag)
|
||||||
from gramps.gen.db import DbTxn
|
from gramps.gen.db import DbTxn, CLASS_TO_KEY_MAP
|
||||||
from gramps.gen.config import config
|
from gramps.gen.config import config
|
||||||
from gramps.gen.utils.id import create_id
|
from gramps.gen.utils.id import create_id
|
||||||
from gramps.gen.utils.db import family_name
|
from gramps.gen.utils.db import family_name
|
||||||
@@ -224,6 +224,22 @@ class Check(tool.BatchTool):
|
|||||||
checker.check_tag_references()
|
checker.check_tag_references()
|
||||||
checker.check_checksum()
|
checker.check_checksum()
|
||||||
checker.check_media_sourceref()
|
checker.check_media_sourceref()
|
||||||
|
|
||||||
|
# for bsddb the check_backlinks doesn't work in 'batch' mode because
|
||||||
|
# the table used for backlinks is closed.
|
||||||
|
with DbTxn(_("Check Backlink Integrity"), self.db,
|
||||||
|
batch=False) as checker.trans:
|
||||||
|
checker.check_backlinks()
|
||||||
|
|
||||||
|
# rebuilding reference maps needs to be done outside of a transaction
|
||||||
|
# to avoid nesting transactions.
|
||||||
|
if checker.bad_backlinks:
|
||||||
|
checker.progress.set_pass(_('Rebuilding reference maps...'), 6)
|
||||||
|
logging.info('Rebuilding reference maps...')
|
||||||
|
self.db.reindex_reference_map(checker.callback)
|
||||||
|
else:
|
||||||
|
logging.info(' OK: no backlink problems found')
|
||||||
|
|
||||||
self.db.enable_signals()
|
self.db.enable_signals()
|
||||||
self.db.request_rebuild()
|
self.db.request_rebuild()
|
||||||
|
|
||||||
@@ -273,6 +289,7 @@ class CheckIntegrity:
|
|||||||
self.replaced_sourceref = []
|
self.replaced_sourceref = []
|
||||||
self.place_errors = 0
|
self.place_errors = 0
|
||||||
self.duplicated_gramps_ids = 0
|
self.duplicated_gramps_ids = 0
|
||||||
|
self.bad_backlinks = 0
|
||||||
self.text = StringIO()
|
self.text = StringIO()
|
||||||
self.last_img_dir = config.get('behavior.addmedia-image-dir')
|
self.last_img_dir = config.get('behavior.addmedia-image-dir')
|
||||||
self.progress = ProgressMeter(_('Checking Database'), '',
|
self.progress = ProgressMeter(_('Checking Database'), '',
|
||||||
@@ -1215,6 +1232,61 @@ class CheckIntegrity:
|
|||||||
len(self.invalid_events) == 0:
|
len(self.invalid_events) == 0:
|
||||||
logging.info(' OK: no event problems found')
|
logging.info(' OK: no event problems found')
|
||||||
|
|
||||||
|
def check_backlinks(self):
|
||||||
|
'''Looking for backlink reference problems'''
|
||||||
|
|
||||||
|
total = self.db.get_total()
|
||||||
|
|
||||||
|
self.progress.set_pass(_('Looking for backlink reference problems'),
|
||||||
|
total)
|
||||||
|
logging.info('Looking for backlink reference problems')
|
||||||
|
|
||||||
|
for obj_class in CLASS_TO_KEY_MAP.keys():
|
||||||
|
obj_type = obj_class.lower()
|
||||||
|
for handle in getattr(self.db, "iter_%s_handles" % obj_type)():
|
||||||
|
self.progress.step()
|
||||||
|
pri_obj = getattr(self.db, "get_%s_from_handle"
|
||||||
|
% obj_type)(handle)
|
||||||
|
handle_list = pri_obj.get_referenced_handles_recursively()
|
||||||
|
# check that each reference has a backlink
|
||||||
|
for item in handle_list:
|
||||||
|
bl_list = list(self.db.find_backlink_handles(item[1]))
|
||||||
|
if (obj_class, handle) not in bl_list:
|
||||||
|
# Object has reference with no cooresponding backlink
|
||||||
|
self.bad_backlinks += 1
|
||||||
|
logging.warning(' FAIL: the "%(cls)s" [%(gid)s] '
|
||||||
|
'has a "%(cls2)s" reference'
|
||||||
|
' with no corresponding backlink.',
|
||||||
|
{'gid': pri_obj.gramps_id,
|
||||||
|
'cls': obj_class, 'cls2': item[0]})
|
||||||
|
# Check for backlinks that don't have a reference
|
||||||
|
bl_list = self.db.find_backlink_handles(handle)
|
||||||
|
for item in bl_list:
|
||||||
|
if not getattr(self.db, "has_%s_handle"
|
||||||
|
% item[0].lower())(item[1]):
|
||||||
|
# backlink to object entirely missing
|
||||||
|
self.bad_backlinks += 1
|
||||||
|
logging.warning(' FAIL: the "%(cls)s" [%(gid)s] '
|
||||||
|
'has a backlink to a missing'
|
||||||
|
' "%(cls2)s".',
|
||||||
|
{'gid': pri_obj.gramps_id,
|
||||||
|
'cls': obj_class, 'cls2': item[0]})
|
||||||
|
continue
|
||||||
|
obj = getattr(self.db, "get_%s_from_handle"
|
||||||
|
% item[0].lower())(item[1])
|
||||||
|
handle_list = obj.get_referenced_handles_recursively()
|
||||||
|
if (obj_class, handle) not in handle_list:
|
||||||
|
# backlink to object which doesn't have reference
|
||||||
|
self.bad_backlinks += 1
|
||||||
|
logging.warning(' FAIL: the "%(cls)s" [%(gid)s] '
|
||||||
|
'has a backlink to a "%(cls2)s"'
|
||||||
|
' with no corresponding reference.',
|
||||||
|
{'gid': pri_obj.gramps_id,
|
||||||
|
'cls': obj_class, 'cls2': item[0]})
|
||||||
|
|
||||||
|
def callback(self, *args):
|
||||||
|
self.progress.step()
|
||||||
|
|
||||||
def check_person_references(self):
|
def check_person_references(self):
|
||||||
'''Looking for person reference problems'''
|
'''Looking for person reference problems'''
|
||||||
plist = self.db.get_person_handles()
|
plist = self.db.get_person_handles()
|
||||||
@@ -2300,7 +2372,8 @@ class CheckIntegrity:
|
|||||||
person_references + family_references + place_references +
|
person_references + family_references + place_references +
|
||||||
citation_references + repo_references + media_references +
|
citation_references + repo_references + media_references +
|
||||||
note_references + tag_references + name_format + empty_objs +
|
note_references + tag_references + name_format + empty_objs +
|
||||||
invalid_dates + source_references + dup_gramps_ids)
|
invalid_dates + source_references + dup_gramps_ids +
|
||||||
|
self.bad_backlinks)
|
||||||
|
|
||||||
if errors == 0:
|
if errors == 0:
|
||||||
if uistate:
|
if uistate:
|
||||||
@@ -2635,6 +2708,11 @@ class CheckIntegrity:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.bad_backlinks:
|
||||||
|
self.text.write(_("%d bad backlinks were fixed;\n")
|
||||||
|
% self.bad_backlinks +
|
||||||
|
_("All reference maps have been rebuilt.") + '\n')
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user