Issue 4276: Gramps cli interface for user with non latin characters in name. Mostly Windows.

svn: r15984
This commit is contained in:
Peter Landgren 2010-10-14 11:46:05 +00:00
parent eb8eee7062
commit ed6f4a0080
8 changed files with 157 additions and 38 deletions

View File

@ -34,7 +34,7 @@ msgstr ""
"Project-Id-Version: sv\n" "Project-Id-Version: sv\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-11 13:24+0200\n" "POT-Creation-Date: 2010-10-11 13:24+0200\n"
"PO-Revision-Date: 2010-10-11 13:26+0200\n" "PO-Revision-Date: 2010-10-14 13:44+0200\n"
"Last-Translator: Peter Landgren <peter.talken@telia.com>\n" "Last-Translator: Peter Landgren <peter.talken@telia.com>\n"
"Language-Team: Swedish <kde-i18n-doc@kde.org>\n" "Language-Team: Swedish <kde-i18n-doc@kde.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -2258,7 +2258,7 @@ msgstr ""
"gramps\n" "gramps\n"
"\n" "\n"
"Notera: Dessa exempel gäller för bash-skalet.\n" "Notera: Dessa exempel gäller för bash-skalet.\n"
"Syntaxen kan vara annorlunda ut för andra skall och i Windows.\n" "Syntaxen kan vara annorlunda för andra skal och i Windows.\n"
#: ../src/cli/argparser.py:225 ../src/cli/argparser.py:339 #: ../src/cli/argparser.py:225 ../src/cli/argparser.py:339
msgid "Error parsing the arguments" msgid "Error parsing the arguments"

View File

@ -95,9 +95,11 @@ class ArgHandler(object):
if self.errorfunc: if self.errorfunc:
self.errorfunc(msg1) self.errorfunc(msg1)
else: else:
print msg1 # Need to convert to system file encoding before printing
# For non latin characters in path/file/user names
print msg1.encode(sys.getfilesystemencoding())
if msg2 is not None: if msg2 is not None:
print msg2 print msg2.encode(sys.getfilesystemencoding())
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# Argument parser: sorts out given arguments # Argument parser: sorts out given arguments
@ -118,6 +120,7 @@ class ArgHandler(object):
""" """
if value is None: if value is None:
return None return None
value = Utils.get_unicode_path_from_env_var(value)
db_path = self.__deduce_db_path(value) db_path = self.__deduce_db_path(value)
if db_path: if db_path:
@ -137,6 +140,9 @@ class ArgHandler(object):
Handle the "-i" or "--import" option. Handle the "-i" or "--import" option.
Only Files supported by a plugin can be imported, so not Family Trees. Only Files supported by a plugin can be imported, so not Family Trees.
""" """
# Need to convert path/filename to unicode before opening
# For non latin characters in Windows path/file/user names
value = Utils.get_unicode_path_from_env_var(value)
fname = value fname = value
fullpath = os.path.abspath(os.path.expanduser(fname)) fullpath = os.path.abspath(os.path.expanduser(fname))
if not os.path.exists(fullpath): if not os.path.exists(fullpath):
@ -171,6 +177,9 @@ class ArgHandler(object):
""" """
if self.gui: if self.gui:
return return
# Need to covert path/filename to unicode before openingh
# For non latin characters in Windows path/file/user names
value = Utils.get_unicode_path_from_env_var(value)
fname = value fname = value
fullpath = os.path.abspath(os.path.expanduser(fname)) fullpath = os.path.abspath(os.path.expanduser(fname))
if os.path.exists(fullpath): if os.path.exists(fullpath):
@ -179,8 +188,9 @@ class ArgHandler(object):
{'name' : fullpath}) {'name' : fullpath})
answer = None answer = None
while not answer: while not answer:
answer = raw_input(_('OK to overwrite? (yes/no) ')) answer = raw_input(_('OK to overwrite? (yes/no) ') \
if answer.upper() in ('Y', 'YES', _('YES')): .encode(sys.getfilesystemencoding()))
if answer.upper() in ('Y', 'YES', _('YES').upper()):
self.__error( _("Will overwrite the existing file: %s") self.__error( _("Will overwrite the existing file: %s")
% fullpath) % fullpath)
else: else:
@ -313,7 +323,11 @@ class ArgHandler(object):
self.cl_action(action, options_str) self.cl_action(action, options_str)
for expt in self.exports: for expt in self.exports:
print "Exporting: file %s, format %s." % expt # Need to convert path/filename to str before printing
# For non latin characters in Windows path/file/user names
fn = expt[0].encode(sys.getfilesystemencoding())
fmt = str(expt[1])
print "Exporting: file %s, format %s." % (fn, fmt)
self.cl_export(expt[0], expt[1]) self.cl_export(expt[0], expt[1])
if cleanup: if cleanup:
@ -345,8 +359,8 @@ class ArgHandler(object):
if self.gui: if self.gui:
self.imp_db_path, title = self.dbman.create_new_db_cli() self.imp_db_path, title = self.dbman.create_new_db_cli()
else: else:
self.imp_db_path = Utils.get_empty_tempdir("import_dbdir") self.imp_db_path = Utils.get_empty_tempdir("import_dbdir") \
.encode(sys.getfilesystemencoding())
newdb = gen.db.DbBsddb() newdb = gen.db.DbBsddb()
newdb.write_version(self.imp_db_path) newdb.write_version(self.imp_db_path)
@ -359,7 +373,9 @@ class ArgHandler(object):
sys.exit(0) sys.exit(0)
for imp in self.imports: for imp in self.imports:
print "Importing: file %s, format %s." % imp fn = imp[0].encode(sys.getfilesystemencoding())
fmt = str(imp[1])
print "Importing: file %s, format %s." % (fn, fmt)
self.cl_import(imp[0], imp[1]) self.cl_import(imp[0], imp[1])
def __open_action(self): def __open_action(self):
@ -483,9 +499,9 @@ class ArgHandler(object):
if len(pdata.id) <= 25: if len(pdata.id) <= 25:
print " %s%s- %s" % ( pdata.id, print " %s%s- %s" % ( pdata.id,
" " * (26 - len(pdata.id)), " " * (26 - len(pdata.id)),
pdata.name) pdata.name.encode(sys.getfilesystemencoding()))
else: else:
print " %s\t- %s" % (pdata.id, pdata.name) print " %s\t- %s" % (pdata.id, pdata.name.encode(sys.getfilesystemencoding()))
elif action == "tool": elif action == "tool":
from gui.plug import tool from gui.plug import tool
@ -521,9 +537,9 @@ class ArgHandler(object):
if len(pdata.id) <= 25: if len(pdata.id) <= 25:
print " %s%s- %s" % ( pdata.id, print " %s%s- %s" % ( pdata.id,
" " * (26 - len(pdata.id)), " " * (26 - len(pdata.id)),
pdata.name) pdata.name.encode(sys.getfilesystemencoding()))
else: else:
print " %s\t- %s" % (pdata.id, pdata.name) print " %s\t- %s" % (pdata.id, pdata.name.encode(sys.getfilesystemencoding()))
else: else:
print "Unknown action: %s." % action print "Unknown action: %s." % action
sys.exit(0) sys.exit(0)

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# #
# Gramps - a GTK+/GNOME based genealogy program # Gramps - a GTK+/GNOME based genealogy program
# #
@ -46,14 +47,16 @@ import logging
import const import const
import config import config
from gen.utils.configmanager import safe_eval from gen.utils.configmanager import safe_eval
import Utils
# Note: Make sure to edit const.py POPT_TABLE too! # Note: Make sure to edit const.py.in POPT_TABLE too!
_HELP = _(""" _HELP = _("""
Usage: gramps.py [OPTION...] Usage: gramps.py [OPTION...]
--load-modules=MODULE1,MODULE2,... Dynamic modules to load --load-modules=MODULE1,MODULE2,... Dynamic modules to load
Help options Help options
-?, --help Show this help message -?, --help Show this help message
--usage Display brief usage message
Application options Application options
-O, --open=FAMILY_TREE Open family tree -O, --open=FAMILY_TREE Open family tree
@ -70,6 +73,51 @@ Application options
-v, --version Show versions and settings -v, --version Show versions and settings
""") """)
_USAGE = _("""
Example of usage of Gramps command line interface
1. To import four databases (whose formats can be determined from their names)
and then check the resulting database for errors, one may type:
gramps -i file1.ged -i file2.gpkg -i ~/db3.gramps -i file4.wft -a tool -p name=check.
2. To explicitly specify the formats in the above example, append filenames with appropriate -f options:
gramps -i file1.ged -f gedcom -i file2.gpkg -f gramps-pkg -i ~/db3.gramps -f gramps-xml -i file4.wft -f wft -a tool -p name=check.
3. To record the database resulting from all imports, supply -e flag
(use -f if the filename does not allow Gramps to guess the format):
gramps -i file1.ged -i file2.gpkg -e ~/new-package -f gramps-pkg
4. To save any error messages of the above example into files outfile and errfile, run:
gramps -i file1.ged -i file2.dpkg -e ~/new-package -f gramps-pkg >outfile 2>errfile
5. To import three databases and start interactive Gramps session with the result:
gramps -i file1.ged -i file2.gpkg -i ~/db3.gramps
6. To open a database and, based on that data, generate timeline report in PDF format
putting the output into the my_timeline.pdf file:
gramps -O 'Family Tree 1' -a report -p name=timeline,off=pdf,of=my_timeline.pdf
7. To generate a summary of a database:
gramps -O 'Family Tree 1' -a report -p name=summary
8. Listing report options
Use the name=timeline,show=all to find out about all available options for the timeline report.
To find out details of a particular option, use show=option_name , e.g. name=timeline,show=off string.
To learn about available report names, use name=show string.
9. To convert a family tree on the fly to a .gramps xml file:
gramps -O 'Family Tree 1' -e output.gramps -f gramps-xml
10. To generate a web site into an other locale (in german):
LANGUAGE=de_DE; LANG=de_DE.UTF-8 gramps -O 'Family Tree 1' -a report -p name=navwebpage,target=/../de
11. Finally, to start normal interactive session type:
gramps
Note: These examples are for bash shell.
Syntax may be different for other shells and for Windows.
""")
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# ArgParser # ArgParser
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -87,8 +135,7 @@ class ArgParser(object):
4/ -e, --export=FILE, export a family tree in required format, optionally 4/ -e, --export=FILE, export a family tree in required format, optionally
provide -f to indicate format provide -f to indicate format
5/ -f, --format=FORMAT : format after a -i or -e option 5/ -f, --format=FORMAT : format after a -i or -e option
6/ -a, --action: An action (possible: 'check', 'summary', 'report', 6/ -a, --action: An action (possible: 'report', 'tool')
'tool')
7/ -p, --options=OPTIONS_STRING : specify options 7/ -p, --options=OPTIONS_STRING : specify options
8/ -u, --force-unlock: A locked database can be unlocked by giving this 8/ -u, --force-unlock: A locked database can be unlocked by giving this
argument when opening it argument when opening it
@ -124,6 +171,7 @@ class ArgParser(object):
self.list = False self.list = False
self.list_more = False self.list_more = False
self.help = False self.help = False
self.usage = False
self.force_unlock = False self.force_unlock = False
self.errors = [] self.errors = []
@ -146,22 +194,37 @@ class ArgParser(object):
4/ -e, --export: export a family tree in required format, optionally provide 4/ -e, --export: export a family tree in required format, optionally provide
-f to indicate format -f to indicate format
5/ -f, --format=FORMAT : format after a -i or -e option 5/ -f, --format=FORMAT : format after a -i or -e option
6/ -a, --action: An action (possible: 'check', 'summary', 'report', 6/ -a, --action: An action (possible: 'report', 'tool')
'tool')
7/ -p, --options=OPTIONS_STRING : specify options 7/ -p, --options=OPTIONS_STRING : specify options
8/ -u, --force-unlock: A locked database can be unlocked by giving this 8/ -u, --force-unlock: A locked database can be unlocked by giving this
argument when opening it argument when opening it
""" """
try: try:
# Convert arguments to unicode, otherwise getopt will not work
# if a non latin character is used as an option (by mistake).
# getopt will try to treat the first char in an utf-8 sequence. Example:
# -Ärik is '-\xc3\x84rik' and getopt will respond :
# option -\xc3 not recognized
for arg in range(len(self.args) - 1):
self.args[arg+1] = Utils.get_unicode_path_from_env_var(self.args[arg + 1])
options, leftargs = getopt.getopt(self.args[1:], options, leftargs = getopt.getopt(self.args[1:],
const.SHORTOPTS, const.LONGOPTS) const.SHORTOPTS, const.LONGOPTS)
except getopt.GetoptError, msg: except getopt.GetoptError, msg:
# Extract the arguments in the list.
# The % operator replaces the list elements with repr() of the list elemements
# which is OK for latin characters, but not for non latin characters in list elements
cliargs = "[ "
for arg in range(len(self.args) - 1):
cliargs += self.args[arg + 1] + " "
cliargs += "]"
# Must first do str() of the msg object.
msg = unicode(str(msg))
self.errors += [(_('Error parsing the arguments'), self.errors += [(_('Error parsing the arguments'),
str(msg) + '\n' + msg + '\n' +
_("Error parsing the arguments: %s \n" _("Error parsing the arguments: %s \n"
"Type gramps --help for an overview of commands, or " "Type gramps --help for an overview of commands, or "
"read the manual pages.") % self.args[1:])] "read the manual pages.") % cliargs)]
return return
if leftargs: if leftargs:
@ -198,7 +261,7 @@ class ArgParser(object):
self.exports.append((value, family_tree_format)) self.exports.append((value, family_tree_format))
elif option in ( '-a', '--action' ): elif option in ( '-a', '--action' ):
action = value action = value
if action not in ( 'check', 'summary', 'report', 'tool' ): if action not in ( 'report', 'tool' ):
print "Unknown action: %s. Ignoring." % action print "Unknown action: %s. Ignoring." % action
continue continue
options_str = "" options_str = ""
@ -240,7 +303,8 @@ class ArgParser(object):
print "Gramps: no such config setting: '%s'" % setting_name print "Gramps: no such config setting: '%s'" % setting_name
need_to_quit = True need_to_quit = True
else: else:
print "Gramps config settings from %s:" % config.config.filename print "Gramps config settings from %s:" % \
config.config.filename.encode(sys.getfilesystemencoding())
for section in config.config.data: for section in config.config.data:
for setting in config.config.data[section]: for setting in config.config.data[section]:
print "%s.%s=%s" % ( print "%s.%s=%s" % (
@ -248,10 +312,13 @@ class ArgParser(object):
repr(config.config.data[section][setting])) repr(config.config.data[section][setting]))
print print
cleandbg += [opt_ix] cleandbg += [opt_ix]
sys.exit(0)
elif option in ('-h', '-?', '--help'): elif option in ('-h', '-?', '--help'):
self.help = True self.help = True
elif option in ('-u', '--force-unlock'): elif option in ('-u', '--force-unlock'):
self.force_unlock = True self.force_unlock = True
elif option in ('--usage'):
self.usage = True
#clean options list #clean options list
cleandbg.reverse() cleandbg.reverse()
@ -260,10 +327,17 @@ class ArgParser(object):
if len(options) > 0 and self.open is None and self.imports == [] \ if len(options) > 0 and self.open is None and self.imports == [] \
and not (self.list or self.list_more or self.help): and not (self.list or self.list_more or self.help):
# Extract and convert to unicode the arguments in the list.
# The % operator replaces the list elements with repr() of the list elemements
# which is OK for latin characters, but not for non latin characters in list elements
cliargs = "[ "
for arg in range(len(self.args) - 1):
cliargs += Utils.get_unicode_path_from_env_var(self.args[arg + 1]) + " "
cliargs += "]"
self.errors += [(_('Error parsing the arguments'), self.errors += [(_('Error parsing the arguments'),
_("Error parsing the arguments: %s \n" _("Error parsing the arguments: %s \n"
"To use in the command-line mode," \ "To use in the command-line mode," \
"supply at least one input file to process.") % self.args[1:])] "supply at least one input file to process.") % cliargs)]
if need_to_quit: if need_to_quit:
sys.exit(0) sys.exit(0)
@ -302,6 +376,15 @@ class ArgParser(object):
If the user gives the --help or -h option, print the output to terminal. If the user gives the --help or -h option, print the output to terminal.
""" """
if self.help: if self.help:
print _HELP # Convert Help messages to file system encoding before printing
print _HELP.encode(sys.getfilesystemencoding())
sys.exit(0) sys.exit(0)
def print_usage(self):
"""
If the user gives the --usage print the output to terminal.
"""
if self.usage:
# Convert Help messages to file system encoding before printing
print _USAGE.encode(sys.getfilesystemencoding())
sys.exit(0)

View File

@ -299,14 +299,24 @@ def startcli(errors, argparser):
""" """
if errors: if errors:
#already errors encountered. Show first one on terminal and exit #already errors encountered. Show first one on terminal and exit
print _('Error encountered: %s') % errors[0][0] # Convert error message to file system encoding before print
print _(' Details: %s') % errors[0][1] errmsg = _('Error encountered: %s') % errors[0][0]
errmsg = errmsg.encode(sys.getfilesystemencoding())
print errmsg
errmsg = _(' Details: %s') % errors[0][1]
errmsg = errmsg.encode(sys.getfilesystemencoding())
print errmsg
sys.exit(1) sys.exit(1)
if argparser.errors: if argparser.errors:
print _('Error encountered in argument parsing: %s') \ # Convert error message to file system encoding before print
errmsg = _('Error encountered in argument parsing: %s') \
% argparser.errors[0][0] % argparser.errors[0][0]
print _(' Details: %s') % argparser.errors[0][1] errmsg = errmsg.encode(sys.getfilesystemencoding())
print errmsg
errmsg = _(' Details: %s') % argparser.errors[0][1]
errmsg = errmsg.encode(sys.getfilesystemencoding())
print errmsg
sys.exit(1) sys.exit(1)
#we need to keep track of the db state #we need to keep track of the db state

View File

@ -374,9 +374,11 @@ class CommandLineReport(object):
opt = self.options_help[key] opt = self.options_help[key]
# Make the output nicer to read, assume that tab has 8 spaces # Make the output nicer to read, assume that tab has 8 spaces
tabs = '\t' if len(key) < 10 else '\t'*2 tabs = '\t' if len(key) < 10 else '\t'*2
print " %s%s%s (%s)" % (key, tabs, opt[1], opt[0]) optmsg = " %s%s%s (%s)" % (key, tabs, opt[1], opt[0])
print optmsg.encode(sys.getfilesystemencoding())
else: else:
print " %s" % key optmsg = " %s" % key
print optmsg.encode(sys.getfilesystemencoding())
print " Use 'show=option' to see description and acceptable values" print " Use 'show=option' to see description and acceptable values"
elif self.show in self.options_help: elif self.show in self.options_help:
opt = self.options_help[self.show] opt = self.options_help[self.show]
@ -386,9 +388,11 @@ class CommandLineReport(object):
vals = opt[2] vals = opt[2]
if isinstance(vals, (list, tuple)): if isinstance(vals, (list, tuple)):
for val in vals: for val in vals:
print " %s" % val optmsg = " %s" % key
print optmsg.encode(sys.getfilesystemencoding())
else: else:
print " %s" % opt[2] optmsg = " %s" % opt[2]
print optmsg.encode(sys.getfilesystemencoding())
else: else:
#there was a show option given, but the option is invalid #there was a show option given, but the option is invalid

View File

@ -229,7 +229,7 @@ NO_GIVEN = "(%s)" % _("none")
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# Note: Make sure to edit ArgHandler.py _help string too! # Note: Make sure to edit argparser.py _help string too!
# (longName, shortName, type , default, flags, descrip , argDescrip) # (longName, shortName, type , default, flags, descrip , argDescrip)
POPT_TABLE = [ POPT_TABLE = [
("config", 'c', str, None, 0, "Set config setting", ""), ("config", 'c', str, None, 0, "Set config setting", ""),
@ -243,7 +243,7 @@ POPT_TABLE = [
("", 'l', None, None, 0, 'List Family Trees', ""), ("", 'l', None, None, 0, 'List Family Trees', ""),
("", 'L', None, None, 0, 'List Family Tree Details', ""), ("", 'L', None, None, 0, 'List Family Tree Details', ""),
("force-unlock", 'u', None, None, 0, 'Force unlock of family tree', ""), ("force-unlock", 'u', None, None, 0, 'Force unlock of family tree', ""),
("settings", 's', None, None, 0, 'Show settings and versions', ""), ("version", 'v', None, None, 0, 'Show versions and settings', ""),
] ]
LONGOPTS = [ LONGOPTS = [
@ -280,6 +280,7 @@ LONGOPTS = [
"sm-config-prefix=", "sm-config-prefix=",
"sm-disable", "sm-disable",
"sync", "sync",
"usage",
"version", "version",
] ]

View File

@ -275,6 +275,7 @@ def run():
else: else:
#CLI use of GRAMPS #CLI use of GRAMPS
argpars.print_help() argpars.print_help()
argpars.print_usage()
from cli.grampscli import startcli from cli.grampscli import startcli
startcli(error, argpars) startcli(error, argpars)

View File

@ -31,6 +31,7 @@
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from __future__ import with_statement from __future__ import with_statement
import os import os
import sys
import cStringIO import cStringIO
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
@ -552,8 +553,10 @@ class CheckIntegrity(object):
photo_name = Utils.media_path_full(self.db, obj.get_path()) photo_name = Utils.media_path_full(self.db, obj.get_path())
if photo_name is not None and photo_name != "" and not Utils.find_file(photo_name): if photo_name is not None and photo_name != "" and not Utils.find_file(photo_name):
if cl: if cl:
# Convert to file system encoding before prining
fn = os.path.basename(photo_name).encode(sys.getfilesystemencoding())
print "Warning: media file %s was not found." \ print "Warning: media file %s was not found." \
% os.path.basename(photo_name) % fn
self.bad_photo.append(ObjectId) self.bad_photo.append(ObjectId)
else: else:
if missmedia_action == 0: if missmedia_action == 0:
@ -1476,7 +1479,8 @@ class Report(ManagedWindow.ManagedWindow):
def __init__(self, uistate, text, cl=0): def __init__(self, uistate, text, cl=0):
if cl: if cl:
print text # Convert to file system encoding before prining
print text.encode(sys.getfilesystemencoding())
return return
ManagedWindow.ManagedWindow.__init__(self, uistate, [], self) ManagedWindow.ManagedWindow.__init__(self, uistate, [], self)