2011-10-01 03:04:21 +05:30
|
|
|
#
|
|
|
|
# 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
|
2011-10-18 22:08:18 +05:30
|
|
|
from gen.lib import (Person, Family, Event, Place, MediaObject, Citation,
|
|
|
|
Repository)
|
2011-10-01 03:04:21 +05:30
|
|
|
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)
|
2011-11-18 02:23:10 +05:30
|
|
|
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()))
|
2011-10-01 03:04:21 +05:30
|
|
|
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)
|
2011-10-18 22:08:18 +05:30
|
|
|
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)
|
2011-10-01 03:04:21 +05:30
|
|
|
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")
|
|
|
|
}
|