Split CLI from GUI. These changes allow CLI to work without GTK
Part 1. To do: pylint on new files. svn: r12674
This commit is contained in:
@@ -6,7 +6,11 @@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@/cli
|
||||
|
||||
pkgdata_PYTHON = \
|
||||
__init__.py
|
||||
__init__.py \
|
||||
arghandler.py \
|
||||
argparser.py \
|
||||
clidbman.py \
|
||||
grampscli.py
|
||||
|
||||
pkgpyexecdir = @pkgpyexecdir@/cli
|
||||
pkgpythondir = @pkgpythondir@/cli
|
||||
|
||||
@@ -22,3 +22,8 @@
|
||||
"""
|
||||
Package init for the cli package.
|
||||
"""
|
||||
|
||||
from grampscli import startcli, CLIDbLoader, CLIManager
|
||||
from argparser import ArgParser
|
||||
from arghandler import ArgHandler
|
||||
from clidbman import CLIDbManager
|
||||
|
||||
497
src/cli/arghandler.py
Normal file
497
src/cli/arghandler.py
Normal file
@@ -0,0 +1,497 @@
|
||||
#
|
||||
# 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
|
||||
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)
|
||||
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
|
||||
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])
|
||||
|
||||
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:
|
||||
print " %s" % item[0]
|
||||
else:
|
||||
print "Unknown action: %s." % action
|
||||
sys.exit(0)
|
||||
256
src/cli/argparser.py
Normal file
256
src/cli/argparser.py
Normal file
@@ -0,0 +1,256 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
|
||||
# Note: Make sure to edit const.py POPT_TABLE too!
|
||||
_HELP = _("""
|
||||
Usage: gramps.py [OPTION...]
|
||||
--load-modules=MODULE1,MODULE2,... Dynamic modules to load
|
||||
|
||||
Help options
|
||||
-?, --help Show this help message
|
||||
--usage Display brief usage message
|
||||
|
||||
Application options
|
||||
-O, --open=FAMILY_TREE Open family tree
|
||||
-i, --import=FILENAME Import file
|
||||
-e, --export=FILENAME Export file
|
||||
-f, --format=FORMAT Specify format
|
||||
-a, --action=ACTION Specify action
|
||||
-p, --options=OPTIONS_STRING Specify options
|
||||
-d, --debug=LOGGER_NAME Enable debug logs
|
||||
-l List Family Trees
|
||||
-L List Family Trees in Detail
|
||||
-u, --force-unlock Force unlock of family tree
|
||||
""")
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# ArgParser
|
||||
#-------------------------------------------------------------------------
|
||||
class ArgParser(object):
|
||||
"""
|
||||
This class is responsible for parsing the command line arguments (if any)
|
||||
given to gramps, and determining if a GUI or a CLI session must be started.
|
||||
The valid arguments are:
|
||||
|
||||
Possible:
|
||||
1/ FAMTREE : Just the family tree (name or database dir)
|
||||
2/ -O, --open=FAMTREE, Open of a family tree
|
||||
3/ -i, --import=FILE, Import of any format understood by an importer, optionally
|
||||
provide- f to indicate format
|
||||
4/ -e, --export=FILE, export a family tree in required format, optionally provide
|
||||
-f to indicate format
|
||||
5/ -f, --format=FORMAT : format after a -i or -e option
|
||||
5/ -a, --action: An action (possible: 'check', 'summary', 'report',
|
||||
'tool')
|
||||
6/ -u, --force-unlock: A locked database can be unlocked by giving this
|
||||
argument when opening it
|
||||
|
||||
If the filename (no flags) is specified, the interactive session is
|
||||
launched using data from filename.
|
||||
In this mode (filename, no flags), the rest of the arguments is ignored.
|
||||
This is a mode suitable by default for GUI launchers, mime type handlers,
|
||||
and the like
|
||||
|
||||
If no filename or -i option is given, a new interactive session (empty
|
||||
database) is launched, since no data is given anyway.
|
||||
|
||||
If -O or -i option is given, but no -e or -a options are given, an
|
||||
interactive session is launched with the FILE (specified with -i).
|
||||
|
||||
If both input (-O or -i) and processing (-e or -a) options are given,
|
||||
interactive session will not be launched.
|
||||
"""
|
||||
|
||||
def __init__(self, args):
|
||||
"""
|
||||
pass the command line arguments on creation
|
||||
"""
|
||||
self.args = args
|
||||
|
||||
self.open_gui = None
|
||||
self.open = None
|
||||
self.exports = []
|
||||
self.actions = []
|
||||
self.imports = []
|
||||
self.imp_db_path = None
|
||||
self.list = False
|
||||
self.list_more = False
|
||||
self.help = False
|
||||
self.force_unlock = False
|
||||
|
||||
self.errors = []
|
||||
self.parse_args()
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Argument parser: sorts out given arguments
|
||||
#-------------------------------------------------------------------------
|
||||
def parse_args(self):
|
||||
"""
|
||||
Fill in lists with open, exports, imports, and actions options.
|
||||
|
||||
Any errors are added to self.errors
|
||||
|
||||
Possible:
|
||||
1/ Just the family tree (name or database dir)
|
||||
2/ -O, Open of a family tree
|
||||
3/ -i, Import of any format understood by an importer, optionally
|
||||
provide-f to indicate format
|
||||
4/ -e, export a family tree in required format, optionally provide
|
||||
-f to indicate format
|
||||
5/ -a, --action: An action (possible: 'check', 'summary', 'report',
|
||||
'tool')
|
||||
6/ -u, --force-unlock: A locked database can be unlocked by giving this
|
||||
argument when opening it
|
||||
|
||||
"""
|
||||
try:
|
||||
options, leftargs = getopt.getopt(self.args[1:],
|
||||
const.SHORTOPTS, const.LONGOPTS)
|
||||
except getopt.GetoptError, msg:
|
||||
self.errors += [(_('Error parsing the arguments'),
|
||||
str(msg) + '\n' +
|
||||
_("Error parsing the arguments: %s \n"
|
||||
"Type gramps --help for an overview of commands, or "
|
||||
"read the manual pages.") % self.args[1:])]
|
||||
return
|
||||
|
||||
if leftargs:
|
||||
# if there were an argument without option,
|
||||
# use it as a file to open and return
|
||||
self.open_gui = leftargs[0]
|
||||
print "Trying to open: %s ..." % leftargs[0]
|
||||
#see if force open is on
|
||||
for opt_ix in range(len(options)):
|
||||
option, value = options[opt_ix]
|
||||
if option in ('-u', '--force-unlock'):
|
||||
self.force_unlock = True
|
||||
break
|
||||
return
|
||||
|
||||
# Go over all given option and place them into appropriate lists
|
||||
for opt_ix in range(len(options)):
|
||||
option, value = options[opt_ix]
|
||||
if option in ( '-O', '--open'):
|
||||
self.open = value
|
||||
elif option in ( '-i', '--import'):
|
||||
format = None
|
||||
if opt_ix < len(options) - 1 \
|
||||
and options[opt_ix + 1][0] in ( '-f', '--format'):
|
||||
format = options[opt_ix + 1][1]
|
||||
self.imports.append((value, format))
|
||||
elif option in ( '-e', '--export' ):
|
||||
format = None
|
||||
if opt_ix < len(options) - 1 \
|
||||
and options[opt_ix + 1][0] in ( '-f', '--format'):
|
||||
format = options[opt_ix + 1][1]
|
||||
self.exports.append((value, format))
|
||||
elif option in ( '-a', '--action' ):
|
||||
action = value
|
||||
if action not in ( 'check', 'summary', 'report', 'tool' ):
|
||||
print "Unknown action: %s. Ignoring." % action
|
||||
continue
|
||||
options_str = ""
|
||||
if opt_ix < len(options)-1 \
|
||||
and options[opt_ix+1][0] in ( '-p', '--options' ):
|
||||
options_str = options[opt_ix+1][1]
|
||||
self.actions.append((action, options_str))
|
||||
elif option in ('-d', '--debug'):
|
||||
logger = logging.getLogger(value)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
elif option in ('-l',):
|
||||
self.list = True
|
||||
elif option in ('-L',):
|
||||
self.list_more = True
|
||||
elif option in ('-h', '-?', '--help'):
|
||||
self.help = True
|
||||
elif option in ('-u', '--force-unlock'):
|
||||
self.force_unlock = True
|
||||
|
||||
if len(options) > 0 and self.open is None and self.imports == [] \
|
||||
and not (self.list or self.list_more or self.help):
|
||||
self.errors += [(_('Error parsing the arguments'),
|
||||
_("Error parsing the arguments: %s \n"
|
||||
"To use in the command-line mode," \
|
||||
"supply at least one input file to process.") % self.args[1:])]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Determine the need for GUI
|
||||
#-------------------------------------------------------------------------
|
||||
def need_gui(self):
|
||||
"""
|
||||
Determine whether we need a GUI session for the given tasks.
|
||||
"""
|
||||
if self.errors:
|
||||
#errors in argument parsing ==> give cli error, no gui needed
|
||||
return False
|
||||
|
||||
if self.list or self.list_more or self.help:
|
||||
return False
|
||||
|
||||
if self.open_gui:
|
||||
# No-option argument, definitely GUI
|
||||
return True
|
||||
|
||||
# If we have data to work with:
|
||||
if (self.open or self.imports):
|
||||
if (self.exports or self.actions):
|
||||
# have both data and what to do with it => no GUI
|
||||
return False
|
||||
else:
|
||||
# data given, but no action/export => GUI
|
||||
return True
|
||||
|
||||
# No data, can only do GUI here
|
||||
return True
|
||||
|
||||
def print_help(self):
|
||||
if self.help:
|
||||
print _HELP
|
||||
sys.exit(0)
|
||||
|
||||
380
src/cli/clidbman.py
Normal file
380
src/cli/clidbman.py
Normal file
@@ -0,0 +1,380 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2009 Brian G. Matherly
|
||||
# Copyright (C) 2009 Gary Burton
|
||||
#
|
||||
# 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: DbManager.py 12621 2009-06-03 18:39:24Z ldnp $
|
||||
|
||||
"""
|
||||
Provide the management of databases from CLI. This includes opening, renaming,
|
||||
creating, and deleting of databases.
|
||||
"""
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Standard python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
import time
|
||||
from gettext import gettext as _
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# set up logging
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import logging
|
||||
LOG = logging.getLogger(".clidbman")
|
||||
|
||||
from gtk import STOCK_DIALOG_ERROR, STOCK_OPEN
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# gramps modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
import gen.db
|
||||
from gen.plug import PluginManager
|
||||
import Config
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# constants
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
DEFAULT_TITLE = _("Family Tree")
|
||||
NAME_FILE = "name.txt"
|
||||
META_NAME = "meta_data.db"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# CLIDbManager
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class CLIDbManager(object):
|
||||
"""
|
||||
Database manager without GTK functionality, allows users to create and
|
||||
open databases
|
||||
"""
|
||||
def __init__(self, dbstate):
|
||||
self.dbstate = dbstate
|
||||
self.msg = None
|
||||
|
||||
if dbstate:
|
||||
self.active = dbstate.db.get_save_path()
|
||||
else:
|
||||
self.active = None
|
||||
|
||||
self.current_names = []
|
||||
self._populate_cli()
|
||||
|
||||
def empty(self, val):
|
||||
"""Callback that does nothing
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_dbdir_summary(self, file_name):
|
||||
"""
|
||||
Returns (people_count, version_number) of current DB.
|
||||
Returns ("Unknown", "Unknown") if invalid DB or other error.
|
||||
"""
|
||||
from bsddb import dbshelve, db
|
||||
from gen.db import META, PERSON_TBL
|
||||
env = db.DBEnv()
|
||||
flags = db.DB_CREATE | db.DB_PRIVATE |\
|
||||
db.DB_INIT_MPOOL | db.DB_INIT_LOCK |\
|
||||
db.DB_INIT_LOG | db.DB_INIT_TXN | db.DB_THREAD
|
||||
try:
|
||||
env.open(file_name, flags)
|
||||
except:
|
||||
return "Unknown", "Unknown"
|
||||
dbmap1 = dbshelve.DBShelf(env)
|
||||
fname = os.path.join(file_name, META + ".db")
|
||||
try:
|
||||
dbmap1.open(fname, META, db.DB_HASH, db.DB_RDONLY)
|
||||
except:
|
||||
return "Unknown", "Unknown"
|
||||
version = dbmap1.get('version', default=None)
|
||||
dbmap1.close()
|
||||
dbmap2 = dbshelve.DBShelf(env)
|
||||
fname = os.path.join(file_name, PERSON_TBL + ".db")
|
||||
try:
|
||||
dbmap2.open(fname, PERSON_TBL, db.DB_HASH, db.DB_RDONLY)
|
||||
except:
|
||||
env.close()
|
||||
return "Unknown", "Unknown"
|
||||
count = len(dbmap2)
|
||||
dbmap2.close()
|
||||
env.close()
|
||||
return (count, version)
|
||||
|
||||
def family_tree_summary(self):
|
||||
"""
|
||||
Return a list of dictionaries of the known family trees.
|
||||
"""
|
||||
# make the default directory if it does not exist
|
||||
list = []
|
||||
for item in self.current_names:
|
||||
(name, dirpath, path_name, last,
|
||||
tval, enable, stock_id) = item
|
||||
count, version = self.get_dbdir_summary(dirpath)
|
||||
retval = {}
|
||||
retval["Number of people"] = count
|
||||
if enable:
|
||||
retval["Locked?"] = "yes"
|
||||
else:
|
||||
retval["Locked?"] = "no"
|
||||
retval["DB version"] = version
|
||||
retval["Family tree"] = name
|
||||
retval["Path"] = dirpath
|
||||
retval["Last accessed"] = time.strftime('%x %X',
|
||||
time.localtime(tval))
|
||||
list.append( retval )
|
||||
return list
|
||||
|
||||
def _populate_cli(self):
|
||||
""" Get the list of current names in the database dir
|
||||
"""
|
||||
# make the default directory if it does not exist
|
||||
dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
|
||||
make_dbdir(dbdir)
|
||||
|
||||
self.current_names = []
|
||||
|
||||
for dpath in os.listdir(dbdir):
|
||||
dirpath = os.path.join(dbdir, dpath)
|
||||
path_name = os.path.join(dirpath, NAME_FILE)
|
||||
if os.path.isfile(path_name):
|
||||
name = file(path_name).readline().strip()
|
||||
|
||||
(tval, last) = time_val(dirpath)
|
||||
(enable, stock_id) = icon_values(dirpath, self.active,
|
||||
self.dbstate.db.is_open())
|
||||
|
||||
if (stock_id == 'gramps-lock'):
|
||||
last = find_locker_name(dirpath)
|
||||
|
||||
self.current_names.append(
|
||||
(name, os.path.join(dbdir, dpath), path_name,
|
||||
last, tval, enable, stock_id))
|
||||
|
||||
self.current_names.sort()
|
||||
|
||||
def get_family_tree_path(self, name):
|
||||
"""
|
||||
Given a name, return None if name not existing or the path to the
|
||||
database if it is a known database name.
|
||||
"""
|
||||
for data in self.current_names:
|
||||
if data[0] == name:
|
||||
return data[1]
|
||||
return None
|
||||
|
||||
def family_tree_list(self):
|
||||
"""Return a list of name, dirname of the known family trees
|
||||
"""
|
||||
lst = [(x[0], x[1]) for x in self.current_names]
|
||||
return lst
|
||||
|
||||
def __start_cursor(self, msg):
|
||||
"""
|
||||
Do needed things to start import visually, eg busy cursor
|
||||
"""
|
||||
print _('Starting Import, %s') % msg
|
||||
|
||||
def __end_cursor(self):
|
||||
"""
|
||||
Set end of a busy cursor
|
||||
"""
|
||||
print _('Import finished...')
|
||||
|
||||
def _create_new_db_cli(self, title=None):
|
||||
"""
|
||||
Create a new database.
|
||||
"""
|
||||
new_path = find_next_db_dir()
|
||||
|
||||
os.mkdir(new_path)
|
||||
path_name = os.path.join(new_path, NAME_FILE)
|
||||
|
||||
if title is None:
|
||||
name_list = [ name[0] for name in self.current_names ]
|
||||
title = find_next_db_name(name_list)
|
||||
|
||||
name_file = open(path_name, "w")
|
||||
name_file.write(title)
|
||||
name_file.close()
|
||||
|
||||
# write the version number into metadata
|
||||
newdb = gen.db.GrampsDBDir()
|
||||
newdb.write_version(new_path)
|
||||
|
||||
(tval, last) = time_val(new_path)
|
||||
|
||||
self.current_names.append((title, new_path, path_name,
|
||||
last, tval, False, ""))
|
||||
return new_path, title
|
||||
|
||||
def _create_new_db(self, title=None):
|
||||
"""
|
||||
Create a new database, do extra stuff needed
|
||||
"""
|
||||
return self._create_new_db_cli(title)
|
||||
|
||||
def import_new_db(self, filename, callback):
|
||||
"""
|
||||
Attempt to import the provided file into a new database.
|
||||
A new database will only be created if an appropriate importer was
|
||||
found.
|
||||
|
||||
@return: A tuple of (new_path, name) for the new database
|
||||
or (None, None) if no import was performed.
|
||||
"""
|
||||
pmgr = PluginManager.get_instance()
|
||||
(name, ext) = os.path.splitext(os.path.basename(filename))
|
||||
format = ext[1:].lower()
|
||||
|
||||
for plugin in pmgr.get_import_plugins():
|
||||
if format == plugin.get_extension():
|
||||
|
||||
new_path, name = self._create_new_db(name)
|
||||
|
||||
# Create a new database
|
||||
self.__start_cursor(_("Importing data..."))
|
||||
dbclass = gen.db.GrampsDBDir
|
||||
dbase = dbclass()
|
||||
dbase.load(new_path, callback)
|
||||
|
||||
import_function = plugin.get_import_function()
|
||||
import_function(dbase, filename, callback)
|
||||
|
||||
# finish up
|
||||
self.__end_cursor()
|
||||
dbase.close()
|
||||
|
||||
return new_path, name
|
||||
return None, None
|
||||
|
||||
def is_locked(self, dbpath):
|
||||
"""
|
||||
returns True if there is a lock file in the dirpath
|
||||
"""
|
||||
if os.path.isfile(os.path.join(dbpath,"lock")):
|
||||
return True
|
||||
return False
|
||||
|
||||
def needs_recovery(self, dbpath):
|
||||
"""
|
||||
returns True if the database in dirpath needs recovery
|
||||
"""
|
||||
if os.path.isfile(os.path.join(dbpath,"need_recover")):
|
||||
return True
|
||||
return False
|
||||
|
||||
def break_lock(self, dbpath):
|
||||
"""
|
||||
Breaks the lock on a database
|
||||
"""
|
||||
if os.path.exists(os.path.join(dbpath, "lock")):
|
||||
os.unlink(os.path.join(dbpath, "lock"))
|
||||
|
||||
def make_dbdir(dbdir):
|
||||
"""
|
||||
Create the default database directory, as defined by dbdir
|
||||
"""
|
||||
try:
|
||||
if not os.path.isdir(dbdir):
|
||||
os.makedirs(dbdir)
|
||||
except (IOError, OSError), msg:
|
||||
LOG.error(_("Could not make database directory: ") + str(msg))
|
||||
|
||||
def find_next_db_name(name_list):
|
||||
"""
|
||||
Scan the name list, looking for names that do not yet exist.
|
||||
Use the DEFAULT_TITLE as the basis for the database name.
|
||||
"""
|
||||
i = 1
|
||||
while True:
|
||||
title = "%s %d" % (DEFAULT_TITLE, i)
|
||||
if title not in name_list:
|
||||
return title
|
||||
i += 1
|
||||
|
||||
def find_next_db_dir():
|
||||
"""
|
||||
Searches the default directory for the first available default
|
||||
database name. Base the name off the current time. In all actuality,
|
||||
the first should be valid.
|
||||
"""
|
||||
while True:
|
||||
base = "%x" % int(time.time())
|
||||
dbdir = os.path.expanduser(Config.get(Config.DATABASE_PATH))
|
||||
new_path = os.path.join(dbdir, base)
|
||||
if not os.path.isdir(new_path):
|
||||
break
|
||||
return new_path
|
||||
|
||||
def time_val(dirpath):
|
||||
"""
|
||||
Return the last modified time of the database. We do this by looking
|
||||
at the modification time of the meta db file. If this file does not
|
||||
exist, we indicate that database as never modified.
|
||||
"""
|
||||
meta = os.path.join(dirpath, META_NAME)
|
||||
if os.path.isfile(meta):
|
||||
tval = os.stat(meta)[9]
|
||||
last = time.strftime('%x %X', time.localtime(tval))
|
||||
else:
|
||||
tval = 0
|
||||
last = _("Never")
|
||||
return (tval, last)
|
||||
|
||||
def icon_values(dirpath, active, is_open):
|
||||
"""
|
||||
If the directory path is the active path, then return values
|
||||
that indicate to use the icon, and which icon to use.
|
||||
"""
|
||||
if os.path.isfile(os.path.join(dirpath,"need_recover")):
|
||||
return (True, STOCK_DIALOG_ERROR)
|
||||
elif dirpath == active and is_open:
|
||||
return (True, STOCK_OPEN)
|
||||
elif os.path.isfile(os.path.join(dirpath,"lock")):
|
||||
return (True, 'gramps-lock')
|
||||
else:
|
||||
return (False, "")
|
||||
|
||||
def find_locker_name(dirpath):
|
||||
"""
|
||||
Opens the lock file if it exists, reads the contexts which is "USERNAME"
|
||||
and returns the contents, with correct string before "USERNAME",
|
||||
so the message can be printed with correct locale.
|
||||
If a file is encountered with errors, we return 'Unknown'
|
||||
This data can eg be displayed in the time column of the manager
|
||||
"""
|
||||
try:
|
||||
fname = os.path.join(dirpath, "lock")
|
||||
ifile = open(fname)
|
||||
username = ifile.read().strip()
|
||||
last = _("Locked by %s") % username
|
||||
ifile.close()
|
||||
except (OSError, IOError):
|
||||
last = _("Unknown")
|
||||
return last
|
||||
284
src/cli/grampscli.py
Normal file
284
src/cli/grampscli.py
Normal file
@@ -0,0 +1,284 @@
|
||||
#
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2001-2006 Donald N. Allingham
|
||||
# Copyright (C) 2009 Benny Malengier
|
||||
#
|
||||
# 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:gramps_main.py 9912 2008-01-22 09:17:46Z acraphae $
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Python modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from gettext import gettext as _
|
||||
import os
|
||||
import sys
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# GRAMPS modules
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
from BasicUtils import name_displayer
|
||||
import Config
|
||||
import const
|
||||
import Errors
|
||||
import DbState
|
||||
from gen.db import GrampsDBDir
|
||||
from gen.plug import PluginManager
|
||||
import GrampsCfg
|
||||
import RecentFiles
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# CLI DbLoader class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class CLIDbLoader(object):
|
||||
def __init__(self, dbstate):
|
||||
self.dbstate = dbstate
|
||||
|
||||
def _warn(title, warnmessage):
|
||||
print _('WARNING: %s') %warnmessage
|
||||
|
||||
def _errordialog(title, errormessage):
|
||||
"""
|
||||
Show the error. A title for the error and an errormessage
|
||||
"""
|
||||
print _('ERROR: %s') % errormessage
|
||||
sys.exit(1)
|
||||
|
||||
def _dberrordialog(self, msg):
|
||||
self._errordialog( '', _("Low level database corruption detected")
|
||||
+ '\n' +
|
||||
_("GRAMPS has detected a problem in the underlying "
|
||||
"Berkeley database. This can be repaired by from "
|
||||
"the Family Tree Manager. Select the database and "
|
||||
'click on the Repair button') + '\n\n' + str(msg))
|
||||
|
||||
def _begin_progress(self):
|
||||
pass
|
||||
|
||||
def _pulse_progress(self, value):
|
||||
pass
|
||||
|
||||
def read_file(self, filename):
|
||||
"""
|
||||
This method takes care of changing database, and loading the data.
|
||||
In 3.0 we only allow reading of real databases of filetype
|
||||
'x-directory/normal'
|
||||
|
||||
This method should only return on success.
|
||||
Returning on failure makes no sense, because we cannot recover,
|
||||
since database has already beeen changed.
|
||||
Therefore, any errors should raise exceptions.
|
||||
|
||||
On success, return with the disabled signals. The post-load routine
|
||||
should enable signals, as well as finish up with other UI goodies.
|
||||
"""
|
||||
|
||||
if os.path.exists(filename):
|
||||
if not os.access(filename, os.W_OK):
|
||||
mode = "r"
|
||||
self._warn(_('Read only database'),
|
||||
_('You do not have write access '
|
||||
'to the selected file.'))
|
||||
else:
|
||||
mode = "w"
|
||||
else:
|
||||
mode = 'w'
|
||||
|
||||
dbclass = GrampsDBDir
|
||||
|
||||
self.dbstate.change_database(dbclass())
|
||||
self.dbstate.db.disable_signals()
|
||||
|
||||
self._begin_progress()
|
||||
|
||||
try:
|
||||
self.dbstate.db.load(filename, self._pulse_progress, mode)
|
||||
self.dbstate.db.set_save_path(filename)
|
||||
except gen.db.FileVersionDeclineToUpgrade:
|
||||
self.dbstate.no_database()
|
||||
except gen.db.exceptions.FileVersionError, msg:
|
||||
self.dbstate.no_database()
|
||||
self._errordialog( _("Cannot open database"), str(msg))
|
||||
except OSError, msg:
|
||||
self.dbstate.no_database()
|
||||
self._errordialog(
|
||||
_("Could not open file: %s") % filename, str(msg))
|
||||
except Errors.DbError, msg:
|
||||
self.dbstate.no_database()
|
||||
self._dberrordialog(msg)
|
||||
except Exception:
|
||||
self.dbstate.no_database()
|
||||
_LOG.error("Failed to open database.", exc_info=True)
|
||||
return True
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# CLIManager class
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
class CLIManager(object):
|
||||
"""
|
||||
A reduced viewmanager suitable for cli actions.
|
||||
Aim is to manage a database on which to work
|
||||
"""
|
||||
def __init__(self, dbstate, setloader):
|
||||
self.dbstate = dbstate
|
||||
if setloader:
|
||||
self.db_loader = CLIDbLoader(self.dbstate)
|
||||
else:
|
||||
self.db_loader = None
|
||||
self.file_loaded = False
|
||||
self._pmgr = PluginManager.get_instance()
|
||||
|
||||
def open_activate(self, path):
|
||||
"""
|
||||
Open and make a family tree active
|
||||
"""
|
||||
self._read_recent_file(path)
|
||||
|
||||
def _errordialog(title, errormessage):
|
||||
"""
|
||||
Show the error. A title for the error and an errormessage
|
||||
"""
|
||||
print _('ERROR: %s') % errormessage
|
||||
sys.exit(1)
|
||||
|
||||
def _read_recent_file(self, filename):
|
||||
"""
|
||||
Called when a file needs to be loaded
|
||||
"""
|
||||
# A recent database should already have a directory
|
||||
# If not, do nothing, just return. This can be handled better if family tree
|
||||
# delete/rename also updated the recent file menu info in DisplayState.py
|
||||
if not os.path.isdir(filename):
|
||||
self.errordialog(
|
||||
_("Could not load a recent Family Tree."),
|
||||
_("Family Tree does not exist, as it has been deleted."))
|
||||
return
|
||||
|
||||
if self.db_loader.read_file(filename):
|
||||
# Attempt to figure out the database title
|
||||
path = os.path.join(filename, "name.txt")
|
||||
try:
|
||||
ifile = open(path)
|
||||
title = ifile.readline().strip()
|
||||
ifile.close()
|
||||
except:
|
||||
title = filename
|
||||
|
||||
self._post_load_newdb(filename, 'x-directory/normal', title)
|
||||
|
||||
def _post_load_newdb(self, filename, filetype, title=None):
|
||||
"""
|
||||
The method called after load of a new database.
|
||||
Here only CLI stuff is done, inherit this method to add extra stuff
|
||||
"""
|
||||
self._post_load_newdb_nongui(filename, filetype, title)
|
||||
|
||||
def _post_load_newdb_nongui(self, filename, filetype, title=None):
|
||||
"""
|
||||
Called after a new database is loaded.
|
||||
"""
|
||||
if not filename:
|
||||
return
|
||||
|
||||
if filename[-1] == os.path.sep:
|
||||
filename = filename[:-1]
|
||||
name = os.path.basename(filename)
|
||||
if title:
|
||||
name = title
|
||||
|
||||
# This method is for UI stuff when the database has changed.
|
||||
# Window title, recent files, etc related to new file.
|
||||
|
||||
self.dbstate.db.set_save_path(filename)
|
||||
|
||||
# apply preferred researcher if loaded file has none
|
||||
res = self.dbstate.db.get_researcher()
|
||||
owner = GrampsCfg.get_researcher()
|
||||
if res.get_name() == "" and owner.get_name() != "":
|
||||
self.dbstate.db.set_researcher(owner)
|
||||
|
||||
name_displayer.set_name_format(self.dbstate.db.name_formats)
|
||||
fmt_default = Config.get(Config.NAME_FORMAT)
|
||||
if fmt_default < 0:
|
||||
name_displayer.set_default_format(fmt_default)
|
||||
|
||||
self.dbstate.db.enable_signals()
|
||||
self.dbstate.signal_change()
|
||||
|
||||
Config.set(Config.RECENT_FILE, filename)
|
||||
|
||||
try:
|
||||
self.dbstate.change_active_person(self.dbstate.db.find_initial_person())
|
||||
except:
|
||||
pass
|
||||
|
||||
RecentFiles.recent_files(filename, name)
|
||||
self.file_loaded = True
|
||||
|
||||
def do_load_plugins(self):
|
||||
"""
|
||||
Loads the plugins at initialization time. The plugin status window is
|
||||
opened on an error if the user has requested.
|
||||
"""
|
||||
# load plugins
|
||||
|
||||
error = self._pmgr.load_plugins(const.PLUGINS_DIR)
|
||||
error |= self._pmgr.load_plugins(const.USER_PLUGINS)
|
||||
|
||||
return error
|
||||
|
||||
def startcli(errors, argparser):
|
||||
"""
|
||||
Starts a cli session of GRAMPS.
|
||||
errors : errors already encountered
|
||||
argparser : ArgParser instance
|
||||
"""
|
||||
if errors:
|
||||
#already errors encountered. Show first one on terminal and exit
|
||||
print _('Error encountered: %s') % errors[0][0]
|
||||
print _(' Details: %s') % errors[0][1]
|
||||
sys.exit(1)
|
||||
|
||||
if argparser.errors:
|
||||
print _('Error encountered in argument parsing: %s') \
|
||||
% argparser.errors[0][0]
|
||||
print _(' Details: %s') % argparser.errors[0][1]
|
||||
sys.exit(1)
|
||||
|
||||
#we need to keep track of the db state
|
||||
dbstate = DbState.DbState()
|
||||
#we need a manager for the CLI session
|
||||
climanager = CLIManager(dbstate, True)
|
||||
#load the plugins
|
||||
climanager.do_load_plugins()
|
||||
# handle the arguments
|
||||
from arghandler import ArgHandler
|
||||
handler = ArgHandler(dbstate, argparser, climanager)
|
||||
# create a manager to manage the database
|
||||
|
||||
handler.handle_args_cli(climanager)
|
||||
|
||||
sys.exit(0)
|
||||
Reference in New Issue
Block a user