Check&Repair; add checks for 'backlinks' and repair if needed.
This commit is contained in:
parent
12d8b62925
commit
0826ba8752
@ -61,7 +61,7 @@ ngettext = glocale.translation.ngettext # else "nearby" comments are ignored
|
||||
from gramps.gen.lib import (Citation, Event, EventType, Family, Media,
|
||||
Name, Note, Person, Place, Repository, Source,
|
||||
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.utils.id import create_id
|
||||
from gramps.gen.utils.db import family_name
|
||||
@ -224,6 +224,22 @@ class Check(tool.BatchTool):
|
||||
checker.check_tag_references()
|
||||
checker.check_checksum()
|
||||
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.request_rebuild()
|
||||
|
||||
@ -273,6 +289,7 @@ class CheckIntegrity:
|
||||
self.replaced_sourceref = []
|
||||
self.place_errors = 0
|
||||
self.duplicated_gramps_ids = 0
|
||||
self.bad_backlinks = 0
|
||||
self.text = StringIO()
|
||||
self.last_img_dir = config.get('behavior.addmedia-image-dir')
|
||||
self.progress = ProgressMeter(_('Checking Database'), '',
|
||||
@ -1215,6 +1232,61 @@ class CheckIntegrity:
|
||||
len(self.invalid_events) == 0:
|
||||
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):
|
||||
'''Looking for person reference problems'''
|
||||
plist = self.db.get_person_handles()
|
||||
@ -2300,7 +2372,8 @@ class CheckIntegrity:
|
||||
person_references + family_references + place_references +
|
||||
citation_references + repo_references + media_references +
|
||||
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 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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user