gramps/src/cli/arghandler.py

498 lines
19 KiB
Python
Raw Normal View History

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham, A. Roitman
# Copyright (C) 2007-2009 B. Malengier
# Copyright (C) 2008 Lukasz Rymarczyk
# Copyright (C) 2008 Raphael Ackermann
# Copyright (C) 2008 Brian G. Matherly
#
# 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: ArgHandler.py 12559 2009-05-21 17:19:50Z gbritton $
"""
Module responsible for handling the command line arguments for GRAMPS.
"""
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import sys
import getopt
from gettext import gettext as _
import logging
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
import const
2006-03-03 05:40:52 +05:30
import Config
import RecentFiles
import Utils
import gen
from clidbman import CLIDbManager, NAME_FILE, find_locker_name
from PluginUtils import Tool
from gen.plug import PluginManager
from ReportBase import CATEGORY_BOOK, CATEGORY_CODE, cl_report
#-------------------------------------------------------------------------
# ArgHandler
#-------------------------------------------------------------------------
class ArgHandler(object):
"""
This class is responsible for the non GUI handling of commands
The handler is passed a parser object, sanitizes it, and can execute the
actions requested working on a DbState
"""
def __init__(self, dbstate, parser, sessionmanager,
errorfunc=None, gui=False):
self.dbstate = dbstate
self.sm = sessionmanager
self.errorfunc = errorfunc
self.gui = gui
self.dbman = CLIDbManager(self.dbstate)
self.force_unlock = parser.force_unlock
self.open = self.__handle_open_option(parser.open)
2006-04-11 00:00:30 +05:30
self.cl = 0
self.imports = []
self.exports = []
self.sanitize_args(parser.imports, parser.exports)
self.open_gui = parser.open_gui
if self.gui:
self.actions = []
self.list = False
self.list_more = False
self.open_gui = None
else:
self.actions = parser.actions
self.list = parser.list
self.list_more = parser.list_more
self.imp_db_path = None
def error(self, string):
if self.errorfunc:
self.errorfunc(string)
else:
print string
#-------------------------------------------------------------------------
# Argument parser: sorts out given arguments
#-------------------------------------------------------------------------
def sanitize_args(self, importlist, exportlist):
"""
check the lists with open, exports, imports, and actions options.
"""
for (value, format) in importlist:
self.__handle_import_option(value, format)
for (value, format) in exportlist:
self.__handle_export_option(value, format)
def __handle_open_option(self, value):
"""
Handle the "-O" or "--open" option.
"""
if value is None:
return None
db_path = self.__deduce_db_path(value)
if db_path:
# We have a potential database path.
# Check if it is good.
if not self.check_db(db_path, self.force_unlock):
sys.exit(0)
return db_path
else:
self.error( _('Error: Input family tree "%s" does not exist.\n'
"If gedcom, gramps-xml or grdb, use the -i option to "
"import into a family tree instead.") % value)
sys.exit(0)
def __handle_import_option(self, value, format):
"""
Handle the "-i" or "--import" option.
"""
fname = value
fullpath = os.path.abspath(os.path.expanduser(fname))
if not os.path.exists(fullpath):
self.error(_('Error: Import file %s not found.') % fname)
sys.exit(0)
if format is None:
# Guess the file format based on the file extension.
# This will get the lower case extension without a period,
# or an empty string.
format = os.path.splitext(fname)[-1][1:].lower()
pmgr = PluginManager.get_instance()
plugin_found = False
for plugin in pmgr.get_import_plugins():
if format == plugin.get_extension():
plugin_found = True
if plugin_found:
self.imports.append((fname, format))
else:
self.error(_('Error: Unrecognized type: "%(format)s" for '
'import file: %(filename)s') \
% {'format' : format,
'filename' : fname})
sys.exit(0)
def __handle_export_option(self, value, format):
"""
Handle the "-e" or "--export" option.
Note: only the CLI version has export
"""
if self.gui:
return
fname = value
fullpath = os.path.abspath(os.path.expanduser(fname))
if os.path.exists(fullpath):
self.error(_("WARNING: Output file already exist!\n"
"WARNING: It will be overwritten:\n %(name)s") % \
{'name' : fullpath})
answer = None
while not answer:
answer = raw_input(_('OK to overwrite? (yes/no) '))
if answer.upper() in ('Y','YES', _('YES')):
self.error( _("Will overwrite the existing file: %s")
% fullpath)
else:
sys.exit(0)
if format is None:
# Guess the file format based on the file extension.
# This will get the lower case extension without a period,
# or an empty string.
format = os.path.splitext(fname)[-1][1:].lower()
pmgr = PluginManager.get_instance()
plugin_found = False
for plugin in pmgr.get_export_plugins():
if format == plugin.get_extension():
plugin_found = True
if plugin_found:
self.exports.append((fullpath, format))
else:
self.error(_("ERROR: Unrecognized format for export file %s")
% fname)
sys.exit(0)
def __deduce_db_path(self, db_name_or_path):
"""
Attempt to find a database path for the given parameter.
@return: The path to a Gramps DB
or None if a database can not be deduced.
"""
# First, check if this is the name of a family tree
db_path = self.dbman.get_family_tree_path(db_name_or_path)
if db_path is None:
# This is not a known database name.
# Check if the user provided a db path instead.
fullpath = os.path.abspath(os.path.expanduser(db_name_or_path))
if os.path.isdir(fullpath):
# The user provided a directory. Check if it is a valid tree.
name_file_path = os.path.join(fullpath, NAME_FILE)
if os.path.isfile(name_file_path):
db_path = fullpath
return db_path
#-------------------------------------------------------------------------
# Overall argument handler:
# sorts out the sequence and details of operations
#-------------------------------------------------------------------------
def handle_args_gui(self, dbman):
"""
method to handle the arguments that can be given for a GUI session.
Returns the filename of the family tree that should be openend
1/no options: a family tree can be given, if so, this name is tested
and returned. If a filename, it is imported in a new db
and name of new db returned
2/an open option can have been given
"""
if self.open_gui:
# First check if a Gramps database was provided
# (either a database path or a database name)
db_path = self.__deduce_db_path(self.open_gui)
if not db_path:
# Apparently it is not a database. See if it is a file that
# can be imported.
db_path, title = self.dbman.import_new_db(self.open_gui, None)
if db_path:
# Test if not locked or problematic
if not self.check_db(db_path, self.force_unlock):
sys.exit(0)
# Add the file to the recent items
path = os.path.join(db_path, "name.txt")
try:
ifile = open(path)
title = ifile.readline().strip()
ifile.close()
except:
title = db_path
RecentFiles.recent_files(db_path, title)
else:
sys.exit(0)
return db_path
# if not open_gui, parse any command line args. We can only have one
# open argument, and perhaps some import arguments
self.__open_action()
self.__import_action()
def handle_args_cli(self, climan):
"""
Depending on the given arguments, import or open data, launch
session, write files, and/or perform actions.
@param: climan: the manager of a CLI session
@type: CLIManager object
"""
if self.list:
print 'List of known family trees in your database path\n'
for name, dirname in self.dbman.family_tree_list():
print dirname, ', with name ', name
sys.exit(0)
if self.list_more:
print 'GRAMPS Family Trees:'
summary_list = self.dbman.family_tree_summary()
for summary in summary_list:
print "Family Tree \"%s\":" % summary["Family tree"]
for item in summary:
if item != "Family tree":
print " %s: %s" % (item, summary[item])
sys.exit(0)
self.__open_action()
self.__import_action()
for (action, options_str) in self.actions:
print "Performing action: %s." % action
if options_str:
print "Using options string: %s" % options_str
self.cl_action(action, options_str)
for expt in self.exports:
print "Exporting: file %s, format %s." % expt
self.cl_export(expt[0], expt[1])
print "Cleaning up."
# remove files in import db subdir after use
self.dbstate.db.close()
if self.imp_db_path:
Utils.rm_tempdir(self.imp_db_path)
print "Exiting."
sys.exit(0)
def __import_action(self):
if self.imports:
self.cl = bool(self.exports or self.actions or self.cl)
if not self.open:
# Create empty dir for imported database(s)
if self.gui:
self.imp_db_path, title = self.dbman._create_new_db_cli()
else:
self.imp_db_path = Utils.get_empty_tempdir("import_dbdir")
newdb = gen.db.GrampsDBDir()
newdb.write_version(self.imp_db_path)
try:
self.sm.open_activate(self.imp_db_path)
print "Created empty fam tree successfully"
except:
print "Error opening the file."
print "Exiting..."
sys.exit(0)
for imp in self.imports:
print "Importing: file %s, format %s." % imp
self.cl_import(imp[0], imp[1])
def __open_action(self):
if self.open:
# Family Tree to open was given. Open it
# Then go on and process the rest of the command line arguments.
self.cl = bool(self.exports or self.actions)
# we load this file for use
try:
self.sm.open_activate(self.open)
print "Opened successfully!"
except:
print "Error opening the file."
print "Exiting..."
sys.exit(0)
def check_db(self, dbpath, force_unlock = False):
# Test if not locked or problematic
if force_unlock:
self.dbman.break_lock(dbpath)
if self.dbman.is_locked(dbpath):
print _("Database is locked, cannot open it!")
print _(" Info: %s") % find_locker_name(dbpath)
return False
if self.dbman.needs_recovery(dbpath):
print _("Database needs recovery, cannot open it!")
return False
return True
#-------------------------------------------------------------------------
#
# Import handler
#
#-------------------------------------------------------------------------
def cl_import(self, filename, format):
"""
Command-line import routine. Try to import filename using the format.
"""
pmgr = PluginManager.get_instance()
for plugin in pmgr.get_import_plugins():
if format == plugin.get_extension():
import_function = plugin.get_import_function()
import_function(self.dbstate.db, filename, None)
if not self.cl:
if self.imp_db_path:
return self.sm.open_activate(self.imp_db_path)
else:
return self.sm.open_activate(self.open)
#-------------------------------------------------------------------------
#
# Export handler
#
#-------------------------------------------------------------------------
def cl_export(self, filename, format):
"""
Command-line export routine.
Try to write into filename using the format.
"""
pmgr = PluginManager.get_instance()
for plugin in pmgr.get_export_plugins():
if format == plugin.get_extension():
export_function = plugin.get_export_function()
export_function(self.dbstate.db, filename)
#-------------------------------------------------------------------------
#
# Action handler
#
#-------------------------------------------------------------------------
def cl_action(self, action, options_str):
"""
Command-line action routine. Try to perform specified action.
"""
pmgr = PluginManager.get_instance()
if action == 'check':
import Check
checker = Check.CheckIntegrity(self.dbstate.db, None, None)
checker.check_for_broken_family_links()
checker.cleanup_missing_photos(1)
checker.check_parent_relationships()
checker.cleanup_empty_families(0)
errs = checker.build_report(1)
if errs:
checker.report(1)
elif action == 'summary':
import Summary
text = Summary.build_report(self.dbstate.db, None)
print text
2004-12-22 07:26:37 +05:30
elif action == "report":
try:
options_str_dict = dict( [ tuple(chunk.split('='))
for chunk in options_str.split(',') ] )
except:
options_str_dict = {}
print "Ignoring invalid options string."
name = options_str_dict.pop('name', None)
_cl_list = pmgr.get_cl_list()
if name:
for item in _cl_list:
if name == item[0]:
category = item[1]
report_class = item[2]
options_class = item[3]
if category in (CATEGORY_BOOK, CATEGORY_CODE):
options_class(self.dbstate.db, name, category,
options_str_dict)
else:
cl_report(self.dbstate.db, name, category, report_class,
options_class, options_str_dict)
return
# name exists, but is not in the list of valid report names
msg = "Unknown report name."
else:
msg = "Report name not given. Please use -p name=reportname."
print "%s\n Available names are:" % msg
for item in _cl_list:
# Print cli report name ([item[0]) and GUI report name (item[4])
if len(item[0]) <= 25:
print " %s%s- %s" % (item[0],
" " * (26 - len(item[0])), item[4])
else:
print " %s\t- %s" % (item[0], item[4])
2005-12-06 12:08:09 +05:30
elif action == "tool":
try:
options_str_dict = dict( [ tuple(chunk.split('=')) for
chunk in options_str.split(',') ] )
except:
options_str_dict = {}
print "Ignoring invalid options string."
name = options_str_dict.pop('name', None)
_cli_tool_list = pmgr.get_cl_tool_list()
if name:
for item in _cli_tool_list:
if name == item[0]:
category = item[1]
tool_class = item[2]
options_class = item[3]
Tool.cli_tool(self.dbstate, name, category, tool_class,
options_class, options_str_dict)
return
msg = "Unknown tool name."
else:
msg = "Tool name not given. Please use -p name=toolname."
print "%s\n Available names are:" % msg
for item in _cli_tool_list:
2005-12-06 12:08:09 +05:30
print " %s" % item[0]
else:
print "Unknown action: %s." % action
sys.exit(0)