gramps/src/gui/plug/tool.py

312 lines
11 KiB
Python
Raw Normal View History

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2005-2007 Donald N. Allingham
# Copyright (C) 2010 Jakim Friant
#
# 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$
"ToolGeneration Framework"
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
import logging
log = logging.getLogger(".")
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
import Utils
from gen.display.name import displayer as name_displayer
import Errors
from gen.plug._options import (Options, OptionHandler, OptionList,
OptionListCollection)
from gen.plug import (TOOL_DEBUG, TOOL_ANAL, TOOL_DBPROC, TOOL_DBFIX,
TOOL_REVCTL, TOOL_UTILS)
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
tool_categories = {
TOOL_DEBUG : _("Debug"),
TOOL_ANAL : _("Analysis and Exploration"),
TOOL_DBPROC : _("Family Tree Processing"),
TOOL_DBFIX : _("Family Tree Repair"),
TOOL_REVCTL : _("Revision Control"),
TOOL_UTILS : _("Utilities"),
}
#-------------------------------------------------------------------------
#
# Tool
#
#-------------------------------------------------------------------------
class Tool(object):
"""
The Tool base class. This is a base class for generating
customized tools. It cannot be used as is, but it can be easily
sub-classed to create a functional tool.
"""
def __init__(self, dbstate, options_class, name):
from gui.plug import MenuToolOptions
self.db = dbstate.db
try:
if issubclass(options_class, MenuToolOptions):
# FIXME: pass in person_id
self.options = options_class(name, None, dbstate)
else: # must be some kind of class or we get a TypeError
self.options = options_class(name)
except TypeError:
self.options = options_class
self.options.load_previous_values()
def run_tool(self):
pass
class BatchTool(Tool):
"""
Same as Tool, except the warning is displayed about the potential
loss of undo history. Should be used for tools using batch transactions.
"""
def __init__(self, dbstate, options_class, name):
# TODO: should we replace this with a callback?
from QuestionDialog import QuestionDialog2
warn_dialog = QuestionDialog2(
_('Undo history warning'),
_('Proceeding with this tool will erase the undo history '
'for this session. In particular, you will not be able '
'to revert the changes made by this tool or any changes '
'made prior to it.\n\n'
'If you think you may want to revert running this tool, '
'please stop here and backup your database.'),
_('_Proceed with the tool'), _('_Stop'))
if not warn_dialog.run():
self.fail = True
return
Tool.__init__(self, dbstate, options_class, name)
self.fail = False
class ActivePersonTool(Tool):
"""
Same as Tool , except the existence of the active person is checked
and the tool is aborted if no active person exists. Should be used
for tools that depend on active person.
"""
def __init__(self, dbstate, uistate, options_class, name):
if not uistate.get_active('Person'):
# TODO: should we replace this with a callback?
from QuestionDialog import ErrorDialog
ErrorDialog(_('Active person has not been set'),
_('You must select an active person for this '
'tool to work properly.'))
self.fail = True
return
Tool.__init__(self, dbstate, options_class, name)
self.fail = False
#------------------------------------------------------------------------
#
# Command-line tool
#
#------------------------------------------------------------------------
class CommandLineTool(object):
"""
Provide a way to run tool from the command line.
"""
def __init__(self, database, name, category, option_class, options_str_dict,
noopt=False):
self.database = database
self.category = category
self.option_class = option_class(name)
self.option_class.load_previous_values()
self.show = options_str_dict.pop('show', None)
self.options_str_dict = options_str_dict
self.init_options(noopt)
self.parse_option_str()
self.show_options()
def init_options(self, noopt):
self.options_dict = {'id' : ''}
self.options_help = {'id' : ["=ID", "Gramps ID of a central person."], }
if noopt:
return
# Add tool-specific options
for key in self.option_class.handler.options_dict:
if key not in self.options_dict:
self.options_dict[key] = self.option_class.handler.options_dict[key]
# Add help for tool-specific options
for key in self.option_class.options_help:
if key not in self.options_help:
self.options_help[key] = self.option_class.options_help[key]
def parse_option_str(self):
for opt in self.options_str_dict:
if opt in self.options_dict:
converter = Utils.get_type_converter(self.options_dict[opt])
self.options_dict[opt] = converter(self.options_str_dict[opt])
self.option_class.handler.options_dict[opt] = self.options_dict[opt]
else:
print "Ignoring unknown option: %s" % opt
person_id = self.options_dict['id']
self.person = self.database.get_person_from_gramps_id(person_id)
id_list = []
for person in self.database.iter_people():
id_list.append("%s\t%s" % (
person.get_gramps_id(),
name_displayer.display(person)))
self.options_help['id'].append(id_list)
self.options_help['id'].append(False)
def show_options(self):
if not self.show:
return
elif self.show == 'all':
print " Available options:"
for key in self.options_dict:
print " %s" % key
print " Use 'show=option' to see description and acceptable values"
elif self.show in self.options_dict:
print ' %s%s\t%s' % (self.show,
self.options_help[self.show][0],
self.options_help[self.show][1])
print " Available values are:"
vals = self.options_help[self.show][2]
if isinstance(vals, (list, tuple)):
if self.options_help[self.show][3]:
for num in range(len(vals)):
print " %d\t%s" % (num, vals[num])
else:
for val in vals:
print " %s" % val
else:
print " %s" % self.options_help[self.show][2]
else:
self.show = None
#------------------------------------------------------------------------
#
# Generic task functions for tools
#
#------------------------------------------------------------------------
# Standard GUI tool generic task
def gui_tool(dbstate, uistate, tool_class, options_class, translated_name,
name, category, callback):
"""
tool - task starts the report. The plugin system requires that the
task be in the format of task that takes a database and a person as
its arguments.
"""
try:
tool_class(dbstate, uistate, options_class, name, callback)
except Errors.WindowActiveError:
pass
except:
log.error("Failed to start tool.", exc_info=True)
# Command-line generic task
def cli_tool(dbstate, name, category, tool_class, options_class, options_str_dict):
clt = CommandLineTool(dbstate.db, name, category,
options_class, options_str_dict)
# Exit here if show option was given
if clt.show:
return
# run tool
try:
tool_class(dbstate, None, clt.option_class, name, None)
except:
log.error("Failed to start tool.", exc_info=True)
#-------------------------------------------------------------------------
#
# Class handling options for plugins
#
#-------------------------------------------------------------------------
class ToolOptionHandler(OptionHandler):
"""
Implements handling of the options for the plugins.
"""
def __init__(self, module_name, options_dict, person_id=None):
OptionHandler.__init__(self, module_name, options_dict, person_id)
def init_subclass(self):
self.collection_class = OptionListCollection
self.list_class = OptionList
self.filename = const.TOOL_OPTIONS
#------------------------------------------------------------------------
#
# Tool Options class
#
#------------------------------------------------------------------------
class ToolOptions(Options):
"""
Defines options and provides handling interface.
This is a base Options class for the tools. All tools, options
classes should derive from it.
"""
def __init__(self, name, person_id=None):
"""
Initialize the class, performing usual house-keeping tasks.
Subclasses MUST call this in their __init__() method.
"""
self.name = name
self.person_id = person_id
self.options_dict = {}
self.options_help = {}
self.handler = None
def load_previous_values(self):
self.handler = ToolOptionHandler(self.name, self.options_dict, self.person_id)