Merge pull request #56 from belissent/master_test2

Environment variables in mediapath
This commit is contained in:
Doug Blank 2015-08-20 12:23:42 -04:00
commit f8d04b5cb8
6 changed files with 220 additions and 84 deletions

View File

@ -7,7 +7,7 @@
<researcher> <researcher>
<resname>Alex Roitman,,,</resname> <resname>Alex Roitman,,,</resname>
</researcher> </researcher>
<mediapath>/home/pierre/Gramps/master/example/gramps</mediapath> <mediapath>{GRAMPS_RESOURCES}/example/gramps</mediapath>
</header> </header>
<name-formats> <name-formats>
<format number="-1" name="SURNAME, Given (Common)" fmt_str="SURNAME, given (common)" active="1"/> <format number="-1" name="SURNAME, Given (Common)" fmt_str="SURNAME, given (common)" active="1"/>

View File

@ -11,7 +11,7 @@
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
@ -85,16 +85,16 @@ APP_VCARD = ["text/x-vcard", "text/x-vcalendar"]
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
if 'GRAMPSHOME' in os.environ: if 'GRAMPSHOME' in os.environ:
USER_HOME = get_env_var('GRAMPSHOME') USER_HOME = get_env_var('GRAMPSHOME')
HOME_DIR = os.path.join(USER_HOME, 'gramps') HOME_DIR = os.path.join(USER_HOME, 'gramps')
elif 'USERPROFILE' in os.environ: elif 'USERPROFILE' in os.environ:
USER_HOME = get_env_var('USERPROFILE') USER_HOME = get_env_var('USERPROFILE')
if 'APPDATA' in os.environ: if 'APPDATA' in os.environ:
HOME_DIR = os.path.join(get_env_var('APPDATA'), 'gramps') HOME_DIR = os.path.join(get_env_var('APPDATA'), 'gramps')
else: else:
HOME_DIR = os.path.join(USER_HOME, 'gramps') HOME_DIR = os.path.join(USER_HOME, 'gramps')
else: else:
USER_HOME = get_env_var('HOME') USER_HOME = get_env_var('HOME')
HOME_DIR = os.path.join(USER_HOME, '.gramps') HOME_DIR = os.path.join(USER_HOME, '.gramps')
@ -115,9 +115,10 @@ USER_PLUGINS = os.path.join(VERSION_DIR, "plugins")
USER_DIRLIST = (USER_HOME, HOME_DIR, VERSION_DIR, ENV_DIR, TEMP_DIR, THUMB_DIR, USER_DIRLIST = (USER_HOME, HOME_DIR, VERSION_DIR, ENV_DIR, TEMP_DIR, THUMB_DIR,
THUMB_NORMAL, THUMB_LARGE, USER_PLUGINS) THUMB_NORMAL, THUMB_LARGE, USER_PLUGINS)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Paths to python modules - assumes that the root directory is one level # Paths to python modules - assumes that the root directory is one level
# above this one, and that the plugins directory is below the root directory. # above this one, and that the plugins directory is below the root directory.
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -170,6 +171,32 @@ LOGO = os.path.join(IMAGE_DIR, "logo.png")
SPLASH = os.path.join(IMAGE_DIR, "splash.jpg") SPLASH = os.path.join(IMAGE_DIR, "splash.jpg")
LICENSE_FILE = os.path.join(_resources.doc_dir, 'COPYING') LICENSE_FILE = os.path.join(_resources.doc_dir, 'COPYING')
#-------------------------------------------------------------------------
#
# GRAMPS environment variables dictionary
#
#-------------------------------------------------------------------------
ENV = {
"USER_HOME": USER_HOME,
"HOME_DIR": HOME_DIR,
"VERSION": VERSION,
"major_version": major_version,
"VERSION_DIR": VERSION_DIR,
"ENV_DIR": ENV_DIR,
"TEMP_DIR": TEMP_DIR,
"THUMB_DIR": THUMB_DIR,
"THUMB_NORMAL": THUMB_NORMAL,
"THUMB_LARGE": THUMB_LARGE,
"USER_PLUGINS": USER_PLUGINS,
"ROOT_DIR": ROOT_DIR,
"GLADE_DIR": GLADE_DIR,
"PLUGINS_DIR": PLUGINS_DIR,
"WEB_DIR": WEB_DIR,
"DATA_DIR": DATA_DIR,
"IMAGE_DIR": IMAGE_DIR,
}
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Init Localization # Init Localization
@ -192,21 +219,21 @@ COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
"is a personal genealogy program.") "is a personal genealogy program.")
AUTHORS = [ AUTHORS = [
"Alexander Roitman", "Alexander Roitman",
"Benny Malengier", "Benny Malengier",
"Brian Matherly", "Brian Matherly",
"Donald A. Peterson", "Donald A. Peterson",
"Donald N. Allingham", "Donald N. Allingham",
"David Hampton", "David Hampton",
"Martin Hawlisch", "Martin Hawlisch",
"Richard Taylor", "Richard Taylor",
"Tim Waugh", "Tim Waugh",
"John Ralls" "John Ralls"
] ]
AUTHORS_FILE = os.path.join(DATA_DIR, "authors.xml") AUTHORS_FILE = os.path.join(DATA_DIR, "authors.xml")
DOCUMENTERS = [ DOCUMENTERS = [
'Alexander Roitman', 'Alexander Roitman',
] ]
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -232,14 +259,14 @@ ARABIC_SEMICOLON = "؛"
# (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(s) and start Gramps", ""), ("config", 'c', str, None, 0, "Set config setting(s) and start Gramps", ""),
("open", 'O', str, None, 0, "Open family tree", "FAMILY_TREE"), ("open", 'O', str, None, 0, "Open family tree", "FAMILY_TREE"),
("create", 'C', str, None, 0, "Create or Open family tree", "FAMILY_TREE"), ("create", 'C', str, None, 0, "Create or Open family tree", "FAMILY_TREE"),
("import", 'i', str, None, 0, "Import file", "FILENAME"), ("import", 'i', str, None, 0, "Import file", "FILENAME"),
("export", 'e', str, None, 0, "Export file", "FILENAME"), ("export", 'e', str, None, 0, "Export file", "FILENAME"),
("format", 'f', str, None, 0, 'Specify format', "FORMAT"), ("format", 'f', str, None, 0, 'Specify format', "FORMAT"),
("action", 'a', str, None, 0, 'Specify action', "ACTION"), ("action", 'a', str, None, 0, 'Specify action', "ACTION"),
("options", 'p', str, None, 0, 'Specify options', "OPTIONS_STRING"), ("options", 'p', str, None, 0, 'Specify options', "OPTIONS_STRING"),
("debug", 'd', str, None, 0, 'Enable debug logs', "LOGGER_NAME"), ("debug", 'd', str, None, 0, 'Enable debug logs', "LOGGER_NAME"),
("", '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', ""),
("show", 's', None, None, 0, "Show config settings", ""), ("show", 's', None, None, 0, "Show config settings", ""),
@ -248,42 +275,42 @@ POPT_TABLE = [
] ]
LONGOPTS = [ LONGOPTS = [
"action=", "action=",
"class=", "class=",
"config=", "config=",
"debug=", "debug=",
"display=", "display=",
"disable-sound", "disable-sound",
"disable-crash-dialog", "disable-crash-dialog",
"enable-sound", "enable-sound",
"espeaker=", "espeaker=",
"export=", "export=",
"force-unlock", "force-unlock",
"format=", "format=",
"gdk-debug=", "gdk-debug=",
"gdk-no-debug=", "gdk-no-debug=",
"gtk-debug=", "gtk-debug=",
"gtk-no-debug=", "gtk-no-debug=",
"gtk-module=", "gtk-module=",
"g-fatal-warnings", "g-fatal-warnings",
"help", "help",
"import=", "import=",
"load-modules=", "load-modules=",
"list" "list"
"name=", "name=",
"oaf-activate-iid=", "oaf-activate-iid=",
"oaf-ior-fd=", "oaf-ior-fd=",
"oaf-private", "oaf-private",
"open=", "open=",
"create=", "create=",
"options=", "options=",
"screen=", "screen=",
"show", "show",
"sm-client-id=", "sm-client-id=",
"sm-config-prefix=", "sm-config-prefix=",
"sm-disable", "sm-disable",
"sync", "sync",
"usage", "usage",
"version", "version",
"qml", "qml",
"yes", "yes",

View File

@ -10,7 +10,7 @@
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
@ -43,7 +43,7 @@ LOG = logging.getLogger(".gen.utils.file")
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from ..constfunc import win, mac, conv_to_unicode, get_env_var from ..constfunc import win, mac, conv_to_unicode, get_env_var
from ..const import TEMP_DIR, USER_HOME, GRAMPS_LOCALE as glocale from ..const import TEMP_DIR, USER_HOME, ENV, GRAMPS_LOCALE as glocale
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -91,7 +91,7 @@ def get_empty_tempdir(dirname):
""" Return path to TEMP_DIR/dirname, a guaranteed empty directory """ Return path to TEMP_DIR/dirname, a guaranteed empty directory
makes intervening directories if required makes intervening directories if required
fails if _file_ by that name already exists, fails if _file_ by that name already exists,
or for inadequate permissions to delete dir/files or create dir(s) or for inadequate permissions to delete dir/files or create dir(s)
""" """
@ -121,10 +121,10 @@ def relative_path(original, base):
return original return original
original = os.path.normpath(original) original = os.path.normpath(original)
base = os.path.normpath(base) base = os.path.normpath(base)
# If the db_dir and obj_dir are on different drives (win only) # If the db_dir and obj_dir are on different drives (win only)
# then there cannot be a relative path. Return original obj_path # then there cannot be a relative path. Return original obj_path
(base_drive, base) = os.path.splitdrive(base) (base_drive, base) = os.path.splitdrive(base)
(orig_drive, orig_name) = os.path.splitdrive(original) (orig_drive, orig_name) = os.path.splitdrive(original)
if base_drive.upper() != orig_drive.upper(): if base_drive.upper() != orig_drive.upper():
return original return original
@ -133,7 +133,7 @@ def relative_path(original, base):
# shared by base and target. # shared by base and target.
base_list = (base).split(os.sep) base_list = (base).split(os.sep)
target_list = (orig_name).split(os.sep) target_list = (orig_name).split(os.sep)
# make sure '/home/person' and 'c:/home/person' both give # make sure '/home/person' and 'c:/home/person' both give
# list ['home', 'person'] # list ['home', 'person']
base_list = [_f for _f in base_list if _f] base_list = [_f for _f in base_list if _f]
target_list = [_f for _f in target_list if _f] target_list = [_f for _f in target_list if _f]
@ -146,14 +146,51 @@ def relative_path(original, base):
rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:]
return os.path.join(*rel_list) return os.path.join(*rel_list)
def expand_path(path, normalize = True):
"""
Expand environment variables in a path
Uses both the environment variables and the GRAMPS environment
The expansion uses the str.format, e.g. "~/{GRAMPSHOME}/{VERSION}/filename.txt"
We make the assumption that the user will not use a path that contain variable names
(it is technically possible to use characters "{", "}" in paths)
"""
environment = dict(os.environ)
environment.update(ENV)
if not 'GRAMPSHOME' in environment:
environment['GRAMPSHOME'] = USER_HOME
path = path.format(**environment)
if normalize:
path = os.path.normcase(os.path.normpath(os.path.abspath(path)))
return path
def media_path(db): def media_path(db):
""" """
Given a database, return the mediapath to use as basedir for media Given a database, return the mediapath to use as basedir for media
""" """
mpath = db.get_mediapath() mpath = db.get_mediapath()
return expand_media_path(mpath, db)
def expand_media_path(mpath, db):
"""
Normalize a mediapath:
- Relative mediapath are considered as relative to the database
- Expand variables, see expand_path
- Convert to absolute path
- Convert slashes and case (on Windows)
"""
# Use home dir if no media_path specified
if mpath is None: if mpath is None:
#use home dir mpath = os.path.abspath(USER_HOME)
mpath = USER_HOME # Expand environment variables
mpath = expand_path(mpath, False)
# Relative mediapath are considered as relative to the database
if not os.path.isabs(mpath):
basepath = db.get_save_path()
if not basepath:
basepath = USER_HOME
mpath = os.path.join(os.path.abspath(basepath), mpath)
# Normalize path
mpath = os.path.normcase(os.path.normpath(os.path.abspath(mpath)))
return mpath return mpath
def media_path_full(db, filename): def media_path_full(db, filename):
@ -178,7 +215,7 @@ def search_for(name):
return 1 return 1
if os.access(name, os.X_OK) and not os.path.isdir(name): if os.access(name, os.X_OK) and not os.path.isdir(name):
return 1 return 1
else: else:
for i in os.environ['PATH'].split(':'): #not win() for i in os.environ['PATH'].split(':'): #not win()
fname = os.path.join(i, name) fname = os.path.join(i, name)
if os.access(fname, os.X_OK) and not os.path.isdir(fname): if os.access(fname, os.X_OK) and not os.path.isdir(fname):

View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2009 B. Malengier
# Copyright (C) 2009 Swoon on bug tracker
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import shutil
import unittest
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import TEMP_DIR, USER_HOME, USER_PLUGINS, VERSION
from gramps.gen.constfunc import get_env_var
from gramps.gen.utils.file import media_path, get_empty_tempdir
from gramps.gen.dbstate import DbState
#-------------------------------------------------------------------------
#
# FileTest class
#
#-------------------------------------------------------------------------
class FileTest(unittest.TestCase):
def test_mediapath(self):
# Create database
dbstate = DbState()
db = dbstate.make_database("bsddb")
path = get_empty_tempdir("utils_file_test")
db.write_version(path)
db.load(path)
dbstate.change_database(db)
# Test without db.mediapath set
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME))))
self.assertTrue(os.path.exists(media_path(db)))
# Test with absolute db.mediapath
db.set_mediapath(os.path.abspath(USER_HOME) + "/test_abs")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME + "/test_abs"))))
# Test with relative db.mediapath
db.set_mediapath("test_rel")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(TEMP_DIR + "/utils_file_test/test_rel"))))
# Test with environment variable
db.set_mediapath("/test/{VERSION}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath("/test/" + VERSION + "/test_var"))))
db.set_mediapath("{USER_PLUGINS}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_PLUGINS + "/test_var"))))
db.set_mediapath("{VERSION}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(TEMP_DIR + "/utils_file_test/" + VERSION + "/test_var"))))
# Test with $GRAMPSHOME environment variable not set
old_env = os.environ.copy()
if 'GRAMPSHOME' in os.environ:
del os.environ['GRAMPSHOME']
db.set_mediapath("{GRAMPSHOME}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME + "/test_var"))))
# Test with $GRAMPSHOME environment variable set
os.environ['GRAMPSHOME'] = "/this/is/a/test"
db.set_mediapath("{GRAMPSHOME}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath("/this/is/a/test/test_var"))))
# Restore environment
os.environ = old_env
#-------------------------------------------------------------------------
#
# main
#
#-------------------------------------------------------------------------
if __name__ == "__main__":
unittest.main()

View File

@ -53,6 +53,7 @@ from gramps.gen.datehandler import get_date_formats
from gramps.gen.display.name import displayer as _nd from gramps.gen.display.name import displayer as _nd
from gramps.gen.display.name import NameDisplayError from gramps.gen.display.name import NameDisplayError
from gramps.gen.utils.alive import update_constants from gramps.gen.utils.alive import update_constants
from gramps.gen.utils.file import media_path
from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword, from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword,
get_translations, get_keyword_from_translation) get_translations, get_keyword_from_translation)
from gramps.gen.lib import Date, FamilyRelType from gramps.gen.lib import Date, FamilyRelType
@ -1460,9 +1461,7 @@ class GrampsPreferences(ConfigureDialog):
_('_Apply'), _('_Apply'),
Gtk.ResponseType.OK) Gtk.ResponseType.OK)
) )
mpath = self.dbstate.db.get_mediapath() mpath = media_path(self.dbstate.db)
if not mpath:
mpath = HOME_DIR
f.set_current_folder(os.path.dirname(mpath)) f.set_current_folder(os.path.dirname(mpath))
status = f.run() status = f.run()

View File

@ -62,7 +62,7 @@ from gramps.gen.errors import GrampsImportError
from gramps.gen.utils.id import create_id from gramps.gen.utils.id import create_id
from gramps.gen.utils.db import family_name from gramps.gen.utils.db import family_name
from gramps.gen.utils.unknown import make_unknown, create_explanation_note from gramps.gen.utils.unknown import make_unknown, create_explanation_note
from gramps.gen.utils.file import create_checksum from gramps.gen.utils.file import create_checksum, media_path, expand_media_path
from gramps.gen.datehandler import parser, set_date from gramps.gen.datehandler import parser, set_date
from gramps.gen.display.name import displayer as name_displayer from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY,
@ -165,35 +165,6 @@ def importData(database, filename, user):
database.readonly = read_only database.readonly = read_only
return info return info
## TODO - WITH MEDIA PATH, IS THIS STILL NEEDED?
## BETTER LEAVE ALL RELATIVE TO NEW RELATIVE PATH
## save_path is in .gramps/dbbase, no good place !
## # copy all local images into <database>.images directory
## db_dir = os.path.abspath(os.path.dirname(database.get_save_path()))
## db_base = os.path.basename(database.get_save_path())
## img_dir = os.path.join(db_dir, db_base)
## first = not os.path.exists(img_dir)
##
## for m_id in database.get_media_object_handles():
## mobject = database.get_object_from_handle(m_id)
## oldfile = mobject.get_path()
## if oldfile and not os.path.isabs(oldfile):
## if first:
## os.mkdir(img_dir)
## first = 0
## newfile = os.path.join(img_dir, oldfile)
##
## try:
## oldfilename = os.path.join(basefile, oldfile)
## shutil.copyfile(oldfilename, newfile)
## try:
## shutil.copystat(oldfilename, newfile)
## except:
## pass
## mobject.set_path(newfile)
## database.commit_media_object(mobject, None, change)
## except (IOError, OSError), msg:
## ErrorDialog(_('Could not copy file'), str(msg))
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -965,14 +936,13 @@ class GrampsParser(UpdateCallback):
person = self.db.get_person_from_handle(self.home) person = self.db.get_person_from_handle(self.home)
self.db.set_default_person_handle(person.handle) self.db.set_default_person_handle(person.handle)
#set media path, this should really do some parsing to convert eg # Set media path
# windows path to unix ? # The paths are normalized before being compared.
if self.mediapath: if self.mediapath:
oldpath = self.db.get_mediapath() if not self.db.get_mediapath():
if not oldpath:
self.db.set_mediapath(self.mediapath) self.db.set_mediapath(self.mediapath)
elif not oldpath == self.mediapath: elif not media_path(self.db) == expand_media_path(self.mediapath, self.db):
self.user.notify_error(_("Could not change media path"), self.user.notify_error(_("Could not change media path"),
_("The opened file has media path %s, which conflicts with" _("The opened file has media path %s, which conflicts with"
" the media path of the Family Tree you import into. " " the media path of the Family Tree you import into. "
"The original media path has been retained. Copy the " "The original media path has been retained. Copy the "