Add MergeCitations tool.
Add note to upgrade information dialogue box to suggest running the tool. Fixes to Person and Family for get citation child list for merging citations. svn: r18228
This commit is contained in:
parent
afefdb09a7
commit
8c36ed6a8e
@ -384,6 +384,10 @@ def gramps_upgrade_16(self):
|
||||
txt += key2string[key] % data_upgradeobject[key2data[key]]
|
||||
except:
|
||||
txt += key2string[key]
|
||||
txt += _("\n\nYou may want to run\n"
|
||||
"Tools -> Family Tree Processing -> Merge\n"
|
||||
"in order to merge citations that contain similar\n"
|
||||
"information")
|
||||
InfoDialog(_('Upgrade Statistics'), txt)
|
||||
|
||||
def upgrade_media_list_16(self, media_list):
|
||||
|
@ -283,8 +283,7 @@ class Family(CitationBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase,
|
||||
:rtype: list
|
||||
"""
|
||||
check_list = self.media_list + self.attribute_list + \
|
||||
self.lds_ord_list + self.child_ref_list + \
|
||||
self.event_ref_list
|
||||
self.lds_ord_list + self.child_ref_list
|
||||
return check_list
|
||||
|
||||
def get_note_child_list(self):
|
||||
|
@ -389,8 +389,7 @@ class Person(CitationBase, NoteBase, AttributeBase, MediaBase,
|
||||
self.address_list +
|
||||
self.attribute_list +
|
||||
self.lds_ord_list +
|
||||
self.person_ref_list +
|
||||
self.event_ref_list
|
||||
self.person_ref_list
|
||||
)
|
||||
|
||||
def get_note_child_list(self):
|
||||
|
288
src/plugins/tool/MergeCitations.py
Normal file
288
src/plugins/tool/MergeCitations.py
Normal file
@ -0,0 +1,288 @@
|
||||
#
|
||||
# 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)
|
||||
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)
|
||||
self.Merge(db, citation_match, citation, trans)
|
||||
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)
|
||||
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")
|
||||
}
|
255
src/plugins/tool/mergecitations.glade
Normal file
255
src/plugins/tool/mergecitations.glade
Normal file
@ -0,0 +1,255 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="message">
|
||||
<property name="modal">True</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">12</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title">
|
||||
<property name="visible">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label44">
|
||||
<property name="visible">True</property>
|
||||
<property name="ypad">10</property>
|
||||
<property name="label" translatable="yes">Please be patient. This may take a while.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">20</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox4">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progressbar1">
|
||||
<property name="visible">True</property>
|
||||
<property name="pulse_step">0.10000000149</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="padding">20</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore1">
|
||||
<columns>
|
||||
<!-- column-name gchararray1 -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkDialog" id="mergecitations">
|
||||
<property name="default_width">350</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="has_separator">False</property>
|
||||
<signal name="delete_event" handler="on_delete_merge_event"/>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title2">
|
||||
<property name="visible">True</property>
|
||||
<property name="justify">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">6</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="table1">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="n_rows">5</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label62">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Match Threshold</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label63">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="label" translatable="yes"><b>Options</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="notes">
|
||||
<property name="label" translatable="yes">Don't merge if citation has notes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="model">liststore1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext1"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area4">
|
||||
<property name="visible">True</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button12">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="destroy_passed_object" object="mergecitations"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button10">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_merge_ok_clicked" object="mergecitations"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button14">
|
||||
<property name="label">gtk-help</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_help_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="0">button12</action-widget>
|
||||
<action-widget response="0">button10</action-widget>
|
||||
<action-widget response="-11">button14</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
@ -517,3 +517,27 @@ toolclass = 'Verify',
|
||||
optionclass = 'VerifyOptions',
|
||||
tool_modes = [TOOL_MODE_GUI, TOOL_MODE_CLI]
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# Merge citations
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
|
||||
register(TOOL,
|
||||
id = 'mergecitations',
|
||||
name = _("Merge Citations"),
|
||||
description = _("Searches the entire database, looking for "
|
||||
"citations that have the same Volume/Page, Date and Confidence."),
|
||||
version = '1.0',
|
||||
gramps_target_version = '3.4',
|
||||
status = STABLE,
|
||||
fname = 'MergeCitations.py',
|
||||
authors = ["Tim G L Lyons"],
|
||||
authors_email = ["gramps-project.org"],
|
||||
category = TOOL_DBPROC,
|
||||
toolclass = 'MergeCitations',
|
||||
optionclass = 'MergeCitationsOptions',
|
||||
tool_modes = [TOOL_MODE_GUI]
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user