gramps/src/plugins/tool/MergeCitations.py

302 lines
12 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2008 Brian G. Matherly
# Copyright (C) 2010 Jakim Friant
# Copyright (C) 2011 Tim G L Lyons
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""Tools/Family Tree Processing/MergeCitations"""
#------------------------------------------------------------------------
#
# Python modules
#
#------------------------------------------------------------------------
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
# GNOME libraries
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from Utils import confidence
import const
from gui.utils import ProgressMeter
from gui.plug import tool
from QuestionDialog import OkDialog
import GrampsDisplay
import DateHandler
import ManagedWindow
from gen.ggettext import sgettext as _
from glade import Glade
from gen.db import DbTxn
from gen.lib import (Person, Family, Event, Place, MediaObject, Citation,
Repository)
from Errors import MergeError
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
ALL_FIELDS = 0
IGNORE_DATE = 1
IGNORE_CONFIDENCE = 2
IGNORE_BOTH = 3
_val2label = {
ALL_FIELDS : _("Match on Page/Volume, Date and Confidence"),
IGNORE_DATE : _("Ignore Date"),
IGNORE_CONFIDENCE : _("Ignore Confidence"),
IGNORE_BOTH : _("Ignore Date and Confidence")
}
WIKI_HELP_PAGE = '%s_-_Tools' % const.URL_MANUAL_PAGE
WIKI_HELP_SEC = _('manual|Merge citations...')
#-------------------------------------------------------------------------
#
# The Actual tool.
#
#-------------------------------------------------------------------------
class MergeCitations(tool.BatchTool,ManagedWindow.ManagedWindow):
def __init__(self, dbstate, uistate, options_class, name, callback=None):
ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__)
self.dbstate = dbstate
self.set_window(gtk.Window(), gtk.Label(), '')
tool.BatchTool.__init__(self, dbstate, options_class, name)
if not self.fail:
uistate.set_busy_cursor(True)
self.run()
uistate.set_busy_cursor(False)
def run(self):
top = Glade(toplevel="mergecitations")
# retrieve options
fields = self.options.handler.options_dict['fields']
dont_merge_notes = self.options.handler.options_dict['dont_merge_notes']
my_menu = gtk.ListStore(str, object)
for val in sorted(_val2label):
my_menu.append([_val2label[val], val])
self.notes_obj = top.get_object("notes")
self.notes_obj.set_active(dont_merge_notes)
self.notes_obj.show()
self.menu = top.get_object("menu")
self.menu.set_model(my_menu)
self.menu.set_active(fields)
window = top.toplevel
window.show()
# self.set_window(window, top.get_object('title'),
# _('Merge citations'))
self.set_window(window, top.get_object('title2'),
"Notes, media objects and data-items of matching "
"citations will be combined.")
top.connect_signals({
"on_merge_ok_clicked" : self.on_merge_ok_clicked,
"destroy_passed_object" : self.cancel,
"on_help_clicked" : self.on_help_clicked,
"on_delete_merge_event" : self.close,
"on_delete_event" : self.close,
})
self.show()
def cancel(self, obj):
"""
on cancel, update the saved values of the options.
"""
fields = self.menu.get_model()[self.menu.get_active()][1]
dont_merge_notes = int(self.notes_obj.get_active())
LOG.debug("cancel fields %d dont_merge_notes %d" %
(fields, dont_merge_notes))
self.options.handler.options_dict['fields'] = fields
self.options.handler.options_dict['dont_merge_notes'] = dont_merge_notes
# Save options
self.options.handler.save_options()
self.close(obj)
def build_menu_names(self, obj):
return (_("Tool settings"),_("Merge citations tool"))
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC)
def on_merge_ok_clicked(self, obj):
"""
Performs the actual merge of citations
(Derived from ExtractCity)
"""
fields = self.menu.get_model()[self.menu.get_active()][1]
dont_merge_notes = int(self.notes_obj.get_active())
LOG.debug("fields %d dont_merge_notes %d" % (fields, dont_merge_notes))
self.options.handler.options_dict['fields'] = fields
self.options.handler.options_dict['dont_merge_notes'] = dont_merge_notes
# Save options
self.options.handler.save_options()
self.progress = ProgressMeter(_('Checking Sources'), '')
self.progress.set_pass(_('Looking for citation fields'),
self.db.get_number_of_citations())
db = self.dbstate.db
db.disable_signals()
num_merges = 0
with DbTxn(_("Merge Citation"), db) as trans:
for handle in db.iter_source_handles():
dict = {}
citation_handle_list = list(db.find_backlink_handles(handle))
for (class_name, citation_handle) in citation_handle_list:
if class_name <> Citation.__name__:
raise MergeError("Encountered an object of type %s "
"that has a citation reference." % class_name)
citation = db.get_citation_from_handle(citation_handle)
key = citation.get_page()
if fields <> IGNORE_DATE and fields <> IGNORE_BOTH:
key += "\n" + DateHandler.get_date(citation)
if fields <> IGNORE_CONFIDENCE and fields <> IGNORE_BOTH:
key += "\n" + \
confidence[citation.get_confidence_level()]
if key in dict and \
(not dont_merge_notes or len(citation.note_list) == 0):
citation_match_handle = dict[key]
citation_match = \
db.get_citation_from_handle(citation_match_handle)
try:
self.Merge(db, citation_match, citation, trans)
except AssertionError:
print "Tool/Family Tree processing/MergeCitations", \
"citation1 gramps_id", citation_match.get_gramps_id(), \
"citation2 gramps_id", citation.get_gramps_id() , \
"citation backlink handles", \
list(db.find_backlink_handles(citation.get_handle()))
num_merges += 1
else:
dict[key] = citation_handle
self.progress.step()
db.enable_signals()
db.request_rebuild()
self.progress.close()
OkDialog(
_("Number of merges done"),
_("%d citations merges" % num_merges),
parent=self.window)
def Merge (self, db, citation1, citation2, trans):
"""
Merges two citations into a single citation.
"""
new_handle = citation1.get_handle()
old_handle = citation2.get_handle()
citation1.merge(citation2)
db.commit_citation(citation1, trans)
for (class_name, handle) in db.find_backlink_handles(
old_handle):
if class_name == Person.__name__:
person = db.get_person_from_handle(handle)
assert(person.has_citation_reference(old_handle))
person.replace_citation_references(old_handle, new_handle)
db.commit_person(person, trans)
elif class_name == Family.__name__:
family = db.get_family_from_handle(handle)
assert(family.has_citation_reference(old_handle))
family.replace_citation_references(old_handle, new_handle)
db.commit_family(family, trans)
elif class_name == Event.__name__:
event = db.get_event_from_handle(handle)
assert(event.has_citation_reference(old_handle))
event.replace_citation_references(old_handle, new_handle)
db.commit_event(event, trans)
elif class_name == Place.__name__:
place = db.get_place_from_handle(handle)
assert(place.has_citation_reference(old_handle))
place.replace_citation_references(old_handle, new_handle)
db.commit_place(place, trans)
elif class_name == MediaObject.__name__:
obj = db.get_object_from_handle(handle)
assert(obj.has_citation_reference(old_handle))
obj.replace_citation_references(old_handle, new_handle)
db.commit_media_object(obj, trans)
elif class_name == Repository.__name__:
repository = db.get_repository_from_handle(handle)
assert(repository.has_citation_reference(old_handle))
repository.replace_citation_references(old_handle, new_handle)
db.commit_repository(repository, trans)
else:
raise MergeError("Encountered an object of type %s that has "
"a citation reference." % class_name)
db.remove_citation(old_handle, trans)
#------------------------------------------------------------------------
#
#
#
#------------------------------------------------------------------------
class MergeCitationsOptions(tool.ToolOptions):
"""
Defines options and provides handling interface.
"""
def __init__(self, name,person_id=None):
tool.ToolOptions.__init__(self, name,person_id)
# Options specific for this report
self.options_dict = {
'fields' : 1,
'dont_merge_notes' : 0,
}
self.options_help = {
'dont_merge_notes' :
("=0/1","Whether to merge citations if they have notes",
["Merge citations with notes",
"Do not merge citations with notes"],
False),
'fields' : ("=num","Threshold for matching",
"Integer number")
}