Migrate code style to Black

This commit is contained in:
Nick Hall
2023-07-31 14:40:59 +01:00
parent 6cb4380d01
commit 41720c5a7e
1109 changed files with 91319 additions and 67252 deletions

View File

@@ -26,4 +26,5 @@ able to run gramps from the source directory without setting PYTHONPATH
From this position, import gramps works great
"""
import gramps.grampsapp as app
app.main()

View File

@@ -5,36 +5,40 @@ from os.path import join
import sys
import site
if getattr(sys, 'frozen', False):
if getattr(sys, "frozen", False):
aio = os.path.dirname(sys.executable)
sys.path.insert(1, aio)
sys.path.insert(1, os.path.join(aio,'lib'))
sys.path.insert(1, os.path.join(aio, "lib"))
sys.path.insert(1, site.getusersitepackages())
environ['PANGOCAIRO_BACKEND'] = 'fontconfig'
environ['SSL_CERT_FILE'] = join(aio, 'ssl/certs/ca-bundle.trust.crt')
environ['GI_TYPELIB_PATH'] = join(aio, 'lib/girepository-1.0')
environ['G_ENABLE_DIAGNOSTIC'] = '0'
environ['G_PARAM_DEPRECATED'] = '0'
environ['GRAMPS_RESOURCES'] = join(aio, 'share')
environ['PATH'] = aio + ';' + aio +'\lib;' + environ['PATH']
environ["PANGOCAIRO_BACKEND"] = "fontconfig"
environ["SSL_CERT_FILE"] = join(aio, "ssl/certs/ca-bundle.trust.crt")
environ["GI_TYPELIB_PATH"] = join(aio, "lib/girepository-1.0")
environ["G_ENABLE_DIAGNOSTIC"] = "0"
environ["G_PARAM_DEPRECATED"] = "0"
environ["GRAMPS_RESOURCES"] = join(aio, "share")
environ["PATH"] = aio + ";" + aio + "\lib;" + environ["PATH"]
def close():
sys.exit()
import atexit
import ctypes
HANDLE = ctypes.windll.kernel32.CreateMutexW(None, 1, "org.gramps-project.gramps")
ERROR = ctypes.GetLastError()
if ERROR == 183: # ERROR_ALREADY_EXISTS:
print('Gramps is already running!', file=sys.stderr)
print("Gramps is already running!", file=sys.stderr)
close()
atexit.register(ctypes.windll.kernel32.CloseHandle, HANDLE)
atexit.register(ctypes.windll.kernel32.ReleaseMutex, HANDLE)
import warnings
warnings.simplefilter("ignore")
import gramps.grampsapp as app
app.run()

View File

@@ -5,19 +5,20 @@ from os.path import join
import sys
import site
if getattr(sys, 'frozen', False):
if getattr(sys, "frozen", False):
aio = os.path.dirname(sys.executable)
sys.path.insert(1, aio)
sys.path.insert(1, os.path.join(aio,'lib'))
sys.path.insert(1, os.path.join(aio, "lib"))
sys.path.insert(1, site.getusersitepackages())
environ['LANG'] = 'en'
environ['PANGOCAIRO_BACKEND'] = 'fontconfig'
environ['SSL_CERT_FILE'] = join(aio, 'ssl/certs/ca-bundle.trust.crt')
environ['GI_TYPELIB_PATH'] = join(aio, 'lib/girepository-1.0')
environ['G_ENABLE_DIAGNOSTIC'] = '0'
environ['G_PARAM_DEPRECATED'] = '0'
environ['GRAMPS_RESOURCES'] = join(aio, 'share')
environ['PATH'] = aio + ';' + aio +'\lib;' + environ['PATH']
environ["LANG"] = "en"
environ["PANGOCAIRO_BACKEND"] = "fontconfig"
environ["SSL_CERT_FILE"] = join(aio, "ssl/certs/ca-bundle.trust.crt")
environ["GI_TYPELIB_PATH"] = join(aio, "lib/girepository-1.0")
environ["G_ENABLE_DIAGNOSTIC"] = "0"
environ["G_PARAM_DEPRECATED"] = "0"
environ["GRAMPS_RESOURCES"] = join(aio, "share")
environ["PATH"] = aio + ";" + aio + "\lib;" + environ["PATH"]
import gramps.grampsapp as app
app.run()

View File

@@ -1,31 +1,33 @@
#!/usr/bin/env python3
'''
"""
grampsw.exe
'''
"""
import os
from os import environ
from os.path import join
import sys
import site
if getattr(sys, 'frozen', False):
if getattr(sys, "frozen", False):
aio = os.path.dirname(sys.executable)
sys.path.insert(1, aio)
sys.path.insert(1, os.path.join(aio,'lib'))
sys.path.insert(1, os.path.join(aio, "lib"))
sys.path.insert(1, site.getusersitepackages())
environ['SSL_CERT_FILE'] = join(aio, 'ssl/certs/ca-bundle.trust.crt')
environ['GI_TYPELIB_PATH'] = join(aio, 'lib/girepository-1.0')
environ['G_ENABLE_DIAGNOSTIC'] = '0'
environ['G_PARAM_DEPRECATED'] = '0'
environ['GRAMPS_RESOURCES'] = join(aio, 'share')
environ['PATH'] = aio + ';' + aio +'\lib;' + environ['PATH']
environ["SSL_CERT_FILE"] = join(aio, "ssl/certs/ca-bundle.trust.crt")
environ["GI_TYPELIB_PATH"] = join(aio, "lib/girepository-1.0")
environ["G_ENABLE_DIAGNOSTIC"] = "0"
environ["G_PARAM_DEPRECATED"] = "0"
environ["GRAMPS_RESOURCES"] = join(aio, "share")
environ["PATH"] = aio + ";" + aio + "\lib;" + environ["PATH"]
import atexit
import ctypes
def close():
''' Show warning dialog if Gramps is already running'''
sys.exit('Gramps is already running!')
"""Show warning dialog if Gramps is already running"""
sys.exit("Gramps is already running!")
HANDLE = ctypes.windll.kernel32.CreateMutexW(None, 1, "org.gramps-project.gramps")
ERROR = ctypes.GetLastError()
@@ -36,4 +38,5 @@ atexit.register(ctypes.windll.kernel32.CloseHandle, HANDLE)
atexit.register(ctypes.windll.kernel32.ReleaseMutex, HANDLE)
import gramps.grampsapp as app
app.main()

View File

@@ -1,7 +1,7 @@
'''
"""
Use with:
python3 setup.py build_exe --no-compress -O1
'''
"""
import sys
import os
import site
@@ -11,141 +11,241 @@ import atexit
import shutil
import zipfile
import cx_Freeze
sys.path.insert(0,'dist')
sys.path.insert(0, "dist")
import gramps
from gramps.version import VERSION_TUPLE
try:
from gramps.version import VERSION_QUALIFIER
except:
VERSION_QUALIFIER = ''
UPX_ALT_PATH = r'UPX'
VERSION_QUALIFIER = ""
UPX_ALT_PATH = r"UPX"
# import logging
# logging.basicConfig(level=logging.DEBUG)
VQ = {'-alpha1': 10, '-alpha2': 11, '-alpha3': 12,
'-beta1': 21, '-beta2': 22, '-beta3': 23,
'-rc1': 22, '': 0}
VQ = {
"-alpha1": 10,
"-alpha2": 11,
"-alpha3": 12,
"-beta1": 21,
"-beta2": 22,
"-beta3": 23,
"-rc1": 22,
"": 0,
}
VERSION = ('.'.join(map(str, VERSION_TUPLE)) + '.' +
str(VQ.get(VERSION_QUALIFIER, 99)))
VERSION = ".".join(map(str, VERSION_TUPLE)) + "." + str(VQ.get(VERSION_QUALIFIER, 99))
COPYRIGHT = "Copyright 2020, Gramps developers. GNU General Public License"
# Prepare a temporay directory
TEMP_DIR = tempfile.TemporaryDirectory()
atexit.register(TEMP_DIR.cleanup)
BASE_DIR = os.path.split(sys.prefix)[1]
SETUP_DIR = os.path.dirname(os.path.realpath(__file__))
SETUP_FILES = ['setup.py', 'gramps.ico', 'grampsc.ico', 'grampsd.ico',
'grampsaioc.py', 'grampsaiocd.py', 'grampsaiow.py']
if '32' in BASE_DIR:
SETUP_FILES.append(''.join(('grampsaio', '32', '.nsi')))
SETUP_FILES = [
"setup.py",
"gramps.ico",
"grampsc.ico",
"grampsd.ico",
"grampsaioc.py",
"grampsaiocd.py",
"grampsaiow.py",
]
if "32" in BASE_DIR:
SETUP_FILES.append("".join(("grampsaio", "32", ".nsi")))
else:
SETUP_FILES.append(''.join(('grampsaio', '64', '.nsi')))
SETUP_FILES.append("".join(("grampsaio", "64", ".nsi")))
INCLUDE_DLL_PATH = os.path.join(sys.exec_prefix, 'bin')
INCLUDE_DLL_PATH = os.path.join(sys.exec_prefix, "bin")
INCLUDE_FILES = []
INCLUDES = ['gi', 'cgi', 'colorsys', 'site']
PACKAGES = ['gi', 'cairo', 'xml', 'bsddb3', 'lxml', 'PIL', 'json', 'csv',
'sqlite3', 'cProfile', 'networkx', 'psycopg2', 'requests', 'logging'
, 'html', 'compileall', 'graphviz', 'pydotplus', 'pygraphviz', 'pydot' ]
EXCLUDES = ['tkinter', 'PyQt5', 'PyQt5.QtCore', 'PyQt5.QtGui', 'pyside'
'PyQt5.QtWidgets', 'sip', 'lib2to3', 'PIL.ImageQt', 'pip', 'distlib'
INCLUDES = ["gi", "cgi", "colorsys", "site"]
PACKAGES = [
"gi",
"cairo",
"xml",
"bsddb3",
"lxml",
"PIL",
"json",
"csv",
"sqlite3",
"cProfile",
"networkx",
"psycopg2",
"requests",
"logging",
"html",
"compileall",
"graphviz",
"pydotplus",
"pygraphviz",
"pydot",
]
EXCLUDES = [
"tkinter",
"PyQt5",
"PyQt5.QtCore",
"PyQt5.QtGui",
"pyside" "PyQt5.QtWidgets",
"sip",
"lib2to3",
"PIL.ImageQt",
"pip",
"distlib",
]
REPLACE_PATHS = [('*', 'AIO/'),
( site.getsitepackages()[0]
+'cx_freeze-5.0-py3.6-mingw.egg/cx_Freeze', 'cx_Freeze/')
REPLACE_PATHS = [
("*", "AIO/"),
(
site.getsitepackages()[0] + "cx_freeze-5.0-py3.6-mingw.egg/cx_Freeze",
"cx_Freeze/",
),
]
MISSING_DLL = ['libgtk-3-0.dll', 'libgtkspell3-3-0.dll', 'libgexiv2-2.dll',
'libgoocanvas-3.0-9.dll', 'libosmgpsmap-1.0-1.dll',
'gswin32c.exe', 'dot.exe', 'libgvplugin_core-6.dll',
'libgvplugin_dot_layout-6.dll', 'libgvplugin_gd-6.dll',
'libgvplugin_pango-6.dll', 'libgvplugin_rsvg-6.dll',
'glib-compile-schemas.exe',
'gdk-pixbuf-query-loaders.exe', 'gtk-update-icon-cache-3.0.exe',
'fc-cache.exe', 'fc-match.exe', 'gspawn-win64-helper-console.exe',
'gspawn-win64-helper.exe', 'libgeocode-glib-0.dll'
MISSING_DLL = [
"libgtk-3-0.dll",
"libgtkspell3-3-0.dll",
"libgexiv2-2.dll",
"libgoocanvas-3.0-9.dll",
"libosmgpsmap-1.0-1.dll",
"gswin32c.exe",
"dot.exe",
"libgvplugin_core-6.dll",
"libgvplugin_dot_layout-6.dll",
"libgvplugin_gd-6.dll",
"libgvplugin_pango-6.dll",
"libgvplugin_rsvg-6.dll",
"glib-compile-schemas.exe",
"gdk-pixbuf-query-loaders.exe",
"gtk-update-icon-cache-3.0.exe",
"fc-cache.exe",
"fc-match.exe",
"gspawn-win64-helper-console.exe",
"gspawn-win64-helper.exe",
"libgeocode-glib-0.dll",
]
BIN_EXCLUDES = ['Qt5Core.dll', 'gdiplus.dll', 'gdiplus']
BIN_EXCLUDES = ["Qt5Core.dll", "gdiplus.dll", "gdiplus"]
from os.path import dirname, basename
import lib2to3
lib23_path = dirname(lib2to3.__file__)
INCLUDE_FILES.append((lib23_path, 'lib/lib2to3'))
import pip
libpip_path = dirname(pip.__file__)
INCLUDE_FILES.append((libpip_path,'lib/pip'))
import distlib
libdistlib_path = dirname(distlib.__file__)
INCLUDE_FILES.append((libdistlib_path,'lib/distlib'))
os.makedirs(os.path.join(BASE_DIR, 'var/cache/fontconfig'), exist_ok=True)
lib23_path = dirname(lib2to3.__file__)
INCLUDE_FILES.append((lib23_path, "lib/lib2to3"))
import pip
libpip_path = dirname(pip.__file__)
INCLUDE_FILES.append((libpip_path, "lib/pip"))
import distlib
libdistlib_path = dirname(distlib.__file__)
INCLUDE_FILES.append((libdistlib_path, "lib/distlib"))
os.makedirs(os.path.join(BASE_DIR, "var/cache/fontconfig"), exist_ok=True)
for file in SETUP_FILES:
INCLUDE_FILES.append((os.path.join(SETUP_DIR, file),
os.path.join('src', file)))
INCLUDE_FILES.append((os.path.join(SETUP_DIR, file), os.path.join("src", file)))
for dll in MISSING_DLL:
INCLUDE_FILES.append((os.path.join(INCLUDE_DLL_PATH, dll),
os.path.join('lib',dll)))
MISSING_LIBS = ['lib/enchant-2', 'lib/gdk-pixbuf-2.0', 'lib/girepository-1.0',
'share/enchant', 'share/glib-2.0/schemas',
'share/xml/iso-codes', 'etc/gtk-3.0',
'etc/ssl/certs', 'etc/ssl/cert.pem', 'etc/fonts', 'lib/gio',
'share/icons/gnome',
'share/icons/hicolor', 'share/icons/gramps.png',
'share/icons/Adwaita/icon-theme.cache',
'share/icons/Adwaita/index.theme', 'share/hunspell'
INCLUDE_FILES.append(
(os.path.join(INCLUDE_DLL_PATH, dll), os.path.join("lib", dll))
)
MISSING_LIBS = [
"lib/enchant-2",
"lib/gdk-pixbuf-2.0",
"lib/girepository-1.0",
"share/enchant",
"share/glib-2.0/schemas",
"share/xml/iso-codes",
"etc/gtk-3.0",
"etc/ssl/certs",
"etc/ssl/cert.pem",
"etc/fonts",
"lib/gio",
"share/icons/gnome",
"share/icons/hicolor",
"share/icons/gramps.png",
"share/icons/Adwaita/icon-theme.cache",
"share/icons/Adwaita/index.theme",
"share/hunspell",
]
ADWAITA = ['8x8', '16x16', '22x22', '24x24', '32x32', '48x48', '64x64',
'96x96', 'cursors'
ADWAITA = [
"8x8",
"16x16",
"22x22",
"24x24",
"32x32",
"48x48",
"64x64",
"96x96",
"cursors",
]
for adw in ADWAITA:
INCLUDE_FILES.append((os.path.join(sys.prefix, 'share/icons/Adwaita', adw),
os.path.join('share/icons/Adwaita', adw)))
INCLUDE_FILES.append(
(
os.path.join(sys.prefix, "share/icons/Adwaita", adw),
os.path.join("share/icons/Adwaita", adw),
)
)
for lib in MISSING_LIBS:
INCLUDE_FILES.append((os.path.join(sys.prefix, lib), lib))
INCLUDE_FILES.append('dist/gramps')
INCLUDE_FILES.append(('dist/gramps-' + '.'.join(map(str, VERSION_TUPLE)) + '.data/data/share','share'))
EXECUTABLES = [cx_Freeze.Executable("grampsaioc.py", base="Console",
target_name='gramps.exe',
icon='gramps.ico', copyright=COPYRIGHT),
cx_Freeze.Executable("grampsaiow.py", base="Win32GUI",
target_name='grampsw.exe',
icon='gramps.ico', copyright=COPYRIGHT),
cx_Freeze.Executable("grampsaiocd.py", base="Console",
target_name='grampsd.exe',
icon='grampsd.ico', copyright=COPYRIGHT)
INCLUDE_FILES.append("dist/gramps")
INCLUDE_FILES.append(
("dist/gramps-" + ".".join(map(str, VERSION_TUPLE)) + ".data/data/share", "share")
)
EXECUTABLES = [
cx_Freeze.Executable(
"grampsaioc.py",
base="Console",
target_name="gramps.exe",
icon="gramps.ico",
copyright=COPYRIGHT,
),
cx_Freeze.Executable(
"grampsaiow.py",
base="Win32GUI",
target_name="grampsw.exe",
icon="gramps.ico",
copyright=COPYRIGHT,
),
cx_Freeze.Executable(
"grampsaiocd.py",
base="Console",
target_name="grampsd.exe",
icon="grampsd.ico",
copyright=COPYRIGHT,
),
]
BUILD_EXE_OPTIONS = {'packages':PACKAGES,
'includes':INCLUDES,
'excludes':EXCLUDES,
'include_files':INCLUDE_FILES,
'bin_includes':MISSING_DLL,
'zip_include_packages': '*', #ZIP_INCLUDE_PACKAGES,
'zip_exclude_packages':EXCLUDES,
'bin_excludes':BIN_EXCLUDES,
'replace_paths':REPLACE_PATHS,
'build_exe':BASE_DIR,
BUILD_EXE_OPTIONS = {
"packages": PACKAGES,
"includes": INCLUDES,
"excludes": EXCLUDES,
"include_files": INCLUDE_FILES,
"bin_includes": MISSING_DLL,
"zip_include_packages": "*", # ZIP_INCLUDE_PACKAGES,
"zip_exclude_packages": EXCLUDES,
"bin_excludes": BIN_EXCLUDES,
"replace_paths": REPLACE_PATHS,
"build_exe": BASE_DIR,
}
BDIST_MSI_OPTIONS = { # uuid.uuid5(uuid.NAMESPACE_DNS, 'GrampsAIO64-5-trunk')
'upgrade_code': '{fbccc04b-7b2e-56d3-8bb7-94d5f68de822}',
"upgrade_code": "{fbccc04b-7b2e-56d3-8bb7-94d5f68de822}",
# uuid.uuid5(uuid.NAMESPACE_DNS, 'v5.0.0-alpha1-476-g473d3aa')
'product_code': '{48304362-2945-5a10-ad60-241f233be4d2}',
'add_to_path': False,
"product_code": "{48304362-2945-5a10-ad60-241f233be4d2}",
"add_to_path": False,
#'initial_target_dir': r'[ProgramFilesFolder]\%s\%s' %
# (company_name, product_name),
}
cx_Freeze.setup(
name="GrampsAIO32" if '32' in BASE_DIR else "GrampsAIO64",
options={"build_exe": BUILD_EXE_OPTIONS, 'bdist_msi': BDIST_MSI_OPTIONS},
name="GrampsAIO32" if "32" in BASE_DIR else "GrampsAIO64",
options={"build_exe": BUILD_EXE_OPTIONS, "bdist_msi": BDIST_MSI_OPTIONS},
version=VERSION,
description="Gramps Genealogy software",
long_description=VERSION_QUALIFIER,
executables=EXECUTABLES)
executables=EXECUTABLES,
)
ZIN = zipfile.ZipFile(os.path.join(BASE_DIR, 'lib/library.zip'), 'r')
ZOUT = zipfile.ZipFile(os.path.join(BASE_DIR, 'lib/pythonx.zip'), 'w')
ZIN = zipfile.ZipFile(os.path.join(BASE_DIR, "lib/library.zip"), "r")
ZOUT = zipfile.ZipFile(os.path.join(BASE_DIR, "lib/pythonx.zip"), "w")
for item in ZIN.infolist():
if not os.path.dirname(item.filename).startswith('gramps'):
if not os.path.dirname(item.filename).startswith("gramps"):
# if '/test' in item.filename or 'test/' in item.filename:
# print("Zip Excluded:", item.filename)
# else:
@@ -154,28 +254,30 @@ for item in ZIN.infolist():
ZOUT.writestr(item, buffer)
ZOUT.close()
ZIN.close()
shutil.move(os.path.join(BASE_DIR, 'lib/pythonx.zip'),
os.path.join(BASE_DIR, 'lib/library.zip'))
shutil.move(
os.path.join(BASE_DIR, "lib/pythonx.zip"), os.path.join(BASE_DIR, "lib/library.zip")
)
if os.path.isfile(UPX_ALT_PATH):
UPX = UPX_ALT_PATH
else:
WHICH = 'where' if os.name == 'nt' else 'which'
WHICH = "where" if os.name == "nt" else "which"
try:
subprocess.check_call([WHICH, 'UPX'])
subprocess.check_call([WHICH, "UPX"])
except subprocess.CalledProcessError:
UPX = None
else:
UPX = 'upx'
UPX = "upx"
if UPX is not None:
ARGS = [UPX, '-7', '--no-progress']
ARGS.extend(os.path.join(BASE_DIR, filename) for filename in
os.listdir(BASE_DIR) if filename == 'name' or
os.path.splitext(filename)[1].lower() in
('.exe', '.dll', '.pyd', '.so') and
os.path.splitext(filename)[0].lower() not in
('libgcc_s_dw2-1', 'gramps', 'grampsw', 'grampsd',
'libwinpthread-1'))
ARGS = [UPX, "-7", "--no-progress"]
ARGS.extend(
os.path.join(BASE_DIR, filename)
for filename in os.listdir(BASE_DIR)
if filename == "name"
or os.path.splitext(filename)[1].lower() in (".exe", ".dll", ".pyd", ".so")
and os.path.splitext(filename)[0].lower()
not in ("libgcc_s_dw2-1", "gramps", "grampsw", "grampsd", "libwinpthread-1")
)
subprocess.call(ARGS)
else:
print("\nUPX not found")

View File

@@ -28,29 +28,29 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -61,11 +61,11 @@ release = '4.2.0'
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('en', 'gramps', u'Gramps Documentation',
['Gramps project'], 1)
]
man_pages = [("en", "gramps", "Gramps Documentation", ["Gramps project"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'cs'
master_doc = "cs"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('cs', 'gramps', u'Gramps Documentation',
[u'.'], 1)
]
man_pages = [("cs", "gramps", "Gramps Documentation", ["."], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'fr'
master_doc = "fr"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%d %B %Y'
today_fmt = "%d %B %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('fr', 'gramps', u'Gramps Documentation',
['Jerome Rapinat'], 1)
]
man_pages = [("fr", "gramps", "Gramps Documentation", ["Jerome Rapinat"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'nl'
master_doc = "nl"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('nl', 'gramps', u'Gramps Documentation',
[u'.'], 1)
]
man_pages = [("nl", "gramps", "Gramps Documentation", ["."], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'pl'
master_doc = "pl"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('pl', 'gramps', u'Gramps Documentation',
[u'.'], 1)
]
man_pages = [("pl", "gramps", "Gramps Documentation", ["."], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'pt_BR'
master_doc = "pt_BR"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('pt_BR', 'gramps', u'Gramps Documentation',
[u'.'], 1)
]
man_pages = [("pt_BR", "gramps", "Gramps Documentation", ["."], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -28,44 +28,44 @@ import sys, os
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'sv'
master_doc = "sv"
# General information about the project.
project = u'Gramps'
copyright = u'2015, Gramps project'
project = "Gramps"
copyright = "2015, Gramps project"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '4.2'
version = "4.2"
# The full version, including alpha/beta/rc tags.
release = '4.2.0'
release = "4.2.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
locale_dirs = './locale'
locale_dirs = "./locale"
gettext_compact = True
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%B %d, %Y'
today_fmt = "%B %d, %Y"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# default_role = None
@@ -82,7 +82,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
html_theme = "default"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -165,7 +165,7 @@ html_theme = 'default'
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output --------------------------------------------------
@@ -173,10 +173,8 @@ htmlhelp_basename = 'Grampsdoc'
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
@@ -184,8 +182,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Gramps.tex', u'Gramps Documentation',
u'.', 'manual'),
("index", "Gramps.tex", "Gramps Documentation", ".", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -213,10 +210,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('sv', 'gramps', u'Gramps Documentation',
[u'.'], 1)
]
man_pages = [("sv", "gramps", "Gramps Documentation", ["."], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -228,9 +222,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Gramps', u'Gramps Documentation',
u'.', 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
"index",
"Gramps",
"Gramps Documentation",
".",
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -42,23 +42,24 @@ try:
except:
DOCUTILS = False
LANGUAGES = ['sv', 'nl', 'pl', 'cs', 'pt_BR', 'fr']
VERSION = '5.0.0'
DATE = ''
LANGUAGES = ["sv", "nl", "pl", "cs", "pt_BR", "fr"]
VERSION = "5.0.0"
DATE = ""
# You can set these variables from the command line.
SPHINXBUILD = 'sphinx-build'
SPHINXBUILD = "sphinx-build"
if sys.platform == 'win32':
pythonCmd = os.path.join(sys.prefix, 'bin', 'python.exe')
sphinxCmd = os.path.join(sys.prefix, 'bin', 'sphinx-build.exe')
elif sys.platform in ['linux2', 'darwin', 'cygwin']:
pythonCmd = os.path.join(sys.prefix, 'bin', 'python')
if sys.platform == "win32":
pythonCmd = os.path.join(sys.prefix, "bin", "python.exe")
sphinxCmd = os.path.join(sys.prefix, "bin", "sphinx-build.exe")
elif sys.platform in ["linux2", "darwin", "cygwin"]:
pythonCmd = os.path.join(sys.prefix, "bin", "python")
sphinxCmd = SPHINXBUILD
else:
print("Update Man ERROR: unknown system, don't know sphinx, ... commands")
sys.exit(0)
def tests():
"""
Testing installed programs.
@@ -67,18 +68,19 @@ def tests():
"""
try:
print("\n=================='python'=============================\n")
os.system('''%(program)s -V''' % {'program': pythonCmd})
os.system("""%(program)s -V""" % {"program": pythonCmd})
except:
print ('Please, install python')
print("Please, install python")
try:
print("\n=================='Sphinx-build'=============================\n")
os.system('''%(program)s''' % {'program': sphinxCmd})
os.system("""%(program)s""" % {"program": sphinxCmd})
except:
print ('Please, install sphinx')
print("Please, install sphinx")
if not DOCUTILS:
print('\nNo docutils support, cannot use -m/--man and -o/--odt arguments.')
print("\nNo docutils support, cannot use -m/--man and -o/--odt arguments.")
def main():
"""
@@ -87,25 +89,45 @@ def main():
"""
parser = ArgumentParser(
description='This program aims to handle documentation'
' and related translated versions.',
description="This program aims to handle documentation"
" and related translated versions.",
)
parser.add_argument("-t", "--test",
action="store_true", dest="test", default=True,
help="test if 'python' and 'sphinx' are properly installed")
parser.add_argument(
"-t",
"--test",
action="store_true",
dest="test",
default=True,
help="test if 'python' and 'sphinx' are properly installed",
)
parser.add_argument("-b", "--build",
action="store_true", dest="build", default=False,
help="build man documentation (via sphinx-build)")
parser.add_argument(
"-b",
"--build",
action="store_true",
dest="build",
default=False,
help="build man documentation (via sphinx-build)",
)
parser.add_argument("-m", "--man",
action="store_true", dest="man", default=False,
help="build man documentation (via docutils)")
parser.add_argument(
"-m",
"--man",
action="store_true",
dest="man",
default=False,
help="build man documentation (via docutils)",
)
parser.add_argument("-o", "--odt",
action="store_true", dest="odt", default=False,
help="build odt documentation (via docutils)")
parser.add_argument(
"-o",
"--odt",
action="store_true",
dest="odt",
default=False,
help="build odt documentation (via docutils)",
)
args = parser.parse_args()
@@ -121,6 +143,7 @@ def main():
if args.odt and DOCUTILS:
odt()
def build():
"""
Build documentation.
@@ -128,28 +151,39 @@ def build():
# testing stage
os.system('''%(program)s -b html . _build/html''' % {'program': sphinxCmd})
os.system('''%(program)s -b htmlhelp . _build/htmlhelp''' % {'program': sphinxCmd})
os.system("""%(program)s -b html . _build/html""" % {"program": sphinxCmd})
os.system("""%(program)s -b htmlhelp . _build/htmlhelp""" % {"program": sphinxCmd})
if DOCUTILS:
os.system('''%(program)s -b man . .''' % {'program': sphinxCmd})
os.system('''%(program)s -b text . _build/text''' % {'program': sphinxCmd})
os.system('''%(program)s -b changes . _build/changes''' % {'program': sphinxCmd})
os.system("""%(program)s -b man . .""" % {"program": sphinxCmd})
os.system("""%(program)s -b text . _build/text""" % {"program": sphinxCmd})
os.system("""%(program)s -b changes . _build/changes""" % {"program": sphinxCmd})
# os.system('''%(program)s -b linkcheck . _build/linkcheck''' % {'program': sphinxCmd})
os.system('''%(program)s -b gettext . _build/gettext''' % {'program': sphinxCmd})
os.system("""%(program)s -b gettext . _build/gettext""" % {"program": sphinxCmd})
for lang in LANGUAGES:
os.system('''%(program)s -b html -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s'''
% {'lang': lang, 'program': sphinxCmd})
os.system('''%(program)s -b htmlhelp -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s'''
% {'lang': lang, 'program': sphinxCmd})
os.system(
"""%(program)s -b html -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s"""
% {"lang": lang, "program": sphinxCmd}
)
os.system(
"""%(program)s -b htmlhelp -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s"""
% {"lang": lang, "program": sphinxCmd}
)
if DOCUTILS:
os.system('''%(program)s -b man %(lang)s %(lang)s'''
% {'lang': lang, 'program': sphinxCmd})
os.system('''%(program)s -b text -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s'''
% {'lang': lang, 'program': sphinxCmd})
os.system(
"""%(program)s -b man %(lang)s %(lang)s"""
% {"lang": lang, "program": sphinxCmd}
)
os.system(
"""%(program)s -b text -D language="%(lang)s" master_doc="%(lang)s" %(lang)s %(lang)s"""
% {"lang": lang, "program": sphinxCmd}
)
# for update/migration
os.system('''%(program)s -b gettext -D language="%(lang)s" master_doc="%(lang)s" . _build/gettext/%(lang)s'''
% {'lang': lang, 'program': sphinxCmd})
os.system(
"""%(program)s -b gettext -D language="%(lang)s" master_doc="%(lang)s" . _build/gettext/%(lang)s"""
% {"lang": lang, "program": sphinxCmd}
)
def man():
"""
@@ -159,11 +193,14 @@ def man():
from docutils.writers import manpage
"""
os.system('''rst2man en.rst gramps.1''')
os.system("""rst2man en.rst gramps.1""")
for lang in LANGUAGES:
os.system('''rst2man %(lang)s/%(lang)s.rst -l %(lang)s %(lang)s/gramps.1'''
% {'lang': lang})
os.system(
"""rst2man %(lang)s/%(lang)s.rst -l %(lang)s %(lang)s/gramps.1"""
% {"lang": lang}
)
def odt():
"""
@@ -173,11 +210,14 @@ def odt():
from docutils.writers.odf_odt import Writer, Reader
"""
os.system('''rst2odt en.rst gramps.odt''')
os.system("""rst2odt en.rst gramps.odt""")
for lang in LANGUAGES:
os.system('''rst2odt %(lang)s/%(lang)s.rst -l %(lang)s %(lang)s/gramps.odt'''
% {'lang': lang})
os.system(
"""rst2odt %(lang)s/%(lang)s.rst -l %(lang)s %(lang)s/gramps.odt"""
% {"lang": lang}
)
if __name__ == "__main__":
main()

View File

@@ -34,40 +34,40 @@ sys.path.append(os.path.abspath(os.pardir))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.todo",
"sphinx.ext.coverage",
"sphinx.ext.viewcode",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = 'Gramps'
copyright = '2001-2019, The Gramps Project'
author = 'Donald N. Allingham'
project = "Gramps"
copyright = "2001-2019, The Gramps Project"
author = "Donald N. Allingham"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '5.1.0'
version = "5.1.0"
# The full version, including alpha/beta/rc tags.
release = '5.1.0'
release = "5.1.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -84,7 +84,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ["_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@@ -102,7 +102,7 @@ exclude_patterns = ['_build']
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
@@ -118,7 +118,7 @@ todo_include_todos = True
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. default: alabaster
html_theme = 'classic'
html_theme = "classic"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -211,20 +211,17 @@ html_static_path = []
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'Grampsdoc'
htmlhelp_basename = "Grampsdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
@@ -233,8 +230,13 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('master_doc', 'Gramps.tex', 'Gramps Documentation',
'The Gramps Project', 'manual'),
(
"master_doc",
"Gramps.tex",
"Gramps Documentation",
"The Gramps Project",
"manual",
),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -262,10 +264,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'gramps', 'Gramps Documentation',
[author], 1)
]
man_pages = [(master_doc, "gramps", "Gramps Documentation", [author], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -277,9 +276,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'Gramps', 'Gramps Documentation',
author, 'Gramps', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"Gramps",
"Gramps Documentation",
author,
"Gramps",
"One line description of project.",
"Miscellaneous",
),
]
# Documents to append as an appendix to all manuals.

View File

@@ -37,18 +37,19 @@ import sys
from argparse import ArgumentParser
# You can set these variables from the command line.
SPHINXBUILD = 'sphinx-build'
SPHINXBUILD = "sphinx-build"
if sys.platform == 'win32':
pythonCmd = os.path.join(sys.prefix, 'bin', 'python.exe')
sphinxCmd = os.path.join(sys.prefix, 'bin', 'sphinx-build.exe')
elif sys.platform in ['linux', 'linux2', 'darwin', 'cygwin']:
pythonCmd = os.path.join(sys.prefix, 'bin', 'python')
if sys.platform == "win32":
pythonCmd = os.path.join(sys.prefix, "bin", "python.exe")
sphinxCmd = os.path.join(sys.prefix, "bin", "sphinx-build.exe")
elif sys.platform in ["linux", "linux2", "darwin", "cygwin"]:
pythonCmd = os.path.join(sys.prefix, "bin", "python")
sphinxCmd = SPHINXBUILD
else:
print("Update Docs ERROR: unknown system, don't know sphinx, ... commands")
sys.exit(0)
def tests():
"""
Testing installed programs.
@@ -57,15 +58,16 @@ def tests():
"""
try:
print("\n=================='python'=============================\n")
os.system('''%(program)s -V''' % {'program': pythonCmd})
os.system("""%(program)s -V""" % {"program": pythonCmd})
except:
print ('Please, install python')
print("Please, install python")
try:
print("\n=================='sphinx-build'=============================\n")
os.system('''%(program)s''' % {'program': sphinxCmd})
os.system("""%(program)s""" % {"program": sphinxCmd})
except:
print ('Please, install sphinx')
print("Please, install sphinx")
def main():
"""
@@ -74,17 +76,26 @@ def main():
"""
parser = ArgumentParser(
description='This program aims to handle manual'
' and translated version.',
description="This program aims to handle manual" " and translated version.",
)
parser.add_argument("-t", "--test",
action="store_true", dest="test", default=True,
help="test if 'python' and 'sphinx' are properly installed")
parser.add_argument(
"-t",
"--test",
action="store_true",
dest="test",
default=True,
help="test if 'python' and 'sphinx' are properly installed",
)
parser.add_argument("-b", "--build",
action="store_true", dest="build", default=True,
help="build documentation")
parser.add_argument(
"-b",
"--build",
action="store_true",
dest="build",
default=True,
help="build documentation",
)
args = parser.parse_args()
@@ -94,6 +105,7 @@ def main():
if args.build:
build()
def build():
"""
Build documentation.
@@ -101,10 +113,11 @@ def build():
# testing stage
os.system('''%(program)s -b html . _build/html''' % {'program': sphinxCmd})
os.system("""%(program)s -b html . _build/html""" % {"program": sphinxCmd})
# os.system('''%(program)s -b changes . _build/changes''' % {'program': sphinxCmd})
# os.system('''%(program)s -b linkcheck . _build/linkcheck''' % {'program': sphinxCmd})
# os.system('''%(program)s -b devhelp . _build/devhelp''' % {'program': sphinxCmd})
if __name__ == "__main__":
main()

View File

@@ -52,9 +52,11 @@ from gramps.gen.plug import BasePluginManager
from gramps.gen.plug.report import CATEGORY_BOOK, CATEGORY_CODE, BookList
from .plug import cl_report, cl_book
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from gramps.gen.config import config
# -------------------------------------------------------------------------
#
# private functions
@@ -110,11 +112,11 @@ def _split_options(options_str):
name += char
else:
# Parsing the value of the option
if value == "" and char == '[':
if value == "" and char == "[":
# print char, "This character begins a list"
in_list = True
value += char
elif in_list == True and char == ']':
elif in_list == True and char == "]":
# print char, "This character ends the list"
in_list = False
value += char
@@ -145,6 +147,7 @@ def _split_options(options_str):
return options_str_dict
# -------------------------------------------------------------------------
# ArgHandler
# -------------------------------------------------------------------------
@@ -155,8 +158,7 @@ class ArgHandler:
actions requested working on a :class:`.DbState`.
"""
def __init__(self, dbstate, parser, sessionmanager,
errorfunc=None, gui=False):
def __init__(self, dbstate, parser, sessionmanager, errorfunc=None, gui=False):
self.dbstate = dbstate
self.smgr = sessionmanager
self.errorfunc = errorfunc
@@ -205,9 +207,9 @@ class ArgHandler:
"""
Check the lists with open, exports, imports, and actions options.
"""
for (value, family_tree_format) in importlist:
for value, family_tree_format in importlist:
self.__handle_import_option(value, family_tree_format)
for (value, family_tree_format) in exportlist:
for value, family_tree_format in exportlist:
self.__handle_export_option(value, family_tree_format)
def __handle_open_option(self, value, create):
@@ -226,22 +228,29 @@ class ArgHandler:
if not self.check_db(db_path, self.force_unlock):
sys.exit(1)
if create:
self.__error(_("Error: Family Tree '%s' already exists.\n"
self.__error(
_(
"Error: Family Tree '%s' already exists.\n"
"The '-C' option cannot be used."
) % value)
)
% value
)
sys.exit(1)
return db_path
elif create:
# create the tree here, and continue
dbid = config.get('database.backend')
db_path, title = self.dbman.create_new_db_cli(title=value,
dbid=dbid)
dbid = config.get("database.backend")
db_path, title = self.dbman.create_new_db_cli(title=value, dbid=dbid)
return db_path
else:
self.__error(_('Error: Input Family Tree "%s" does not exist.\n'
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)
)
% value
)
sys.exit(1)
def __handle_import_option(self, value, family_tree_format):
@@ -251,8 +260,8 @@ class ArgHandler:
"""
fname = value
fullpath = os.path.abspath(os.path.expanduser(fname))
if fname != '-' and not os.path.exists(fullpath):
self.__error(_('Error: Import file %s not found.') % fname)
if fname != "-" and not os.path.exists(fullpath):
self.__error(_("Error: Import file %s not found.") % fname)
sys.exit(1)
if family_tree_format is None:
@@ -270,10 +279,13 @@ class ArgHandler:
if plugin_found:
self.imports.append((fname, family_tree_format))
else:
self.__error(_('Error: Unrecognized type: "%(format)s" for '
'import file: %(filename)s'
) % {'format' : family_tree_format,
'filename' : fname})
self.__error(
_(
'Error: Unrecognized type: "%(format)s" for '
"import file: %(filename)s"
)
% {"format": family_tree_format, "filename": fname}
)
sys.exit(1)
def __handle_export_option(self, value, family_tree_format):
@@ -285,20 +297,27 @@ class ArgHandler:
if self.gui:
return
fname = value
if fname == '-':
fullpath = '-'
if fname == "-":
fullpath = "-"
else:
fullpath = os.path.abspath(os.path.expanduser(fname))
if os.path.exists(fullpath):
message = _("WARNING: Output file already exists!\n"
message = (
_(
"WARNING: Output file already exists!\n"
"WARNING: It will be overwritten:\n %s"
) % fullpath
accepted = self.user.prompt(_('OK to overwrite?'), message,
_('yes'), _('no'),
default_label=_('yes'))
)
% fullpath
)
accepted = self.user.prompt(
_("OK to overwrite?"),
message,
_("yes"),
_("no"),
default_label=_("yes"),
)
if accepted:
self.__error(_("Will overwrite the existing file: %s"
) % fullpath)
self.__error(_("Will overwrite the existing file: %s") % fullpath)
else:
sys.exit(1)
@@ -317,8 +336,7 @@ class ArgHandler:
if plugin_found:
self.exports.append((fullpath, family_tree_format))
else:
self.__error(_("ERROR: Unrecognized format for export file %s"
) % fname)
self.__error(_("ERROR: Unrecognized format for export file %s") % fname)
sys.exit(1)
def __deduce_db_path(self, db_name_or_path):
@@ -368,8 +386,7 @@ class ArgHandler:
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,
self.user)
db_path, title = self.dbman.import_new_db(self.open_gui, self.user)
if db_path:
# Test if not locked or problematic
@@ -402,17 +419,21 @@ class ArgHandler:
"""
# Handle the "-l" List Family Trees option.
if self.list:
print(_('List of known Family Trees in your database path\n'))
print(_("List of known Family Trees in your database path\n"))
for name, dirname in sorted(self.dbman.family_tree_list(),
key=lambda pair: pair[0].lower()):
if (self.database_names is None
or any([(re.match("^" + dbname + "$", name)
or dbname == name)
for dbname in self.database_names])):
print(_('%(full_DB_path)s with name "%(f_t_name)s"'
) % {'full_DB_path' : dirname,
'f_t_name' : name})
for name, dirname in sorted(
self.dbman.family_tree_list(), key=lambda pair: pair[0].lower()
):
if self.database_names is None or any(
[
(re.match("^" + dbname + "$", name) or dbname == name)
for dbname in self.database_names
]
):
print(
_('%(full_DB_path)s with name "%(f_t_name)s"')
% {"full_DB_path": dirname, "f_t_name": name}
)
return
# Handle the "--remove" Family Tree
@@ -428,7 +449,7 @@ class ArgHandler:
# Handle the "-t" List Family Trees, tab delimited option.
if self.list_table:
print(_('Gramps Family Trees:'))
print(_("Gramps Family Trees:"))
summary_list = self.dbman.family_tree_summary(self.database_names)
if not summary_list:
return
@@ -439,9 +460,9 @@ class ArgHandler:
if key != _("Family Tree"):
line_list += [key]
print("\t".join(line_list))
for summary in sorted(summary_list,
key=lambda
sum: sum[_("Family Tree")].lower()):
for summary in sorted(
summary_list, key=lambda sum: sum[_("Family Tree")].lower()
):
line_list = [(_('"%s"') % summary[_("Family Tree")])]
for item in sorted(summary):
if item != _("Family Tree"):
@@ -453,21 +474,18 @@ class ArgHandler:
self.__open_action()
self.__import_action()
for (action, op_string) in self.actions:
print(_("Performing action: %s."
) % action,
file=sys.stderr)
for action, op_string in self.actions:
print(_("Performing action: %s.") % action, file=sys.stderr)
if op_string:
print(_("Using options string: %s"
) % op_string,
file=sys.stderr)
print(_("Using options string: %s") % op_string, file=sys.stderr)
self.cl_action(action, op_string)
for expt in self.exports:
print(_("Exporting: file %(filename)s, format %(format)s."
) % {'filename' : expt[0],
'format' : expt[1]},
file=sys.stderr)
print(
_("Exporting: file %(filename)s, format %(format)s.")
% {"filename": expt[0], "format": expt[1]},
file=sys.stderr,
)
self.cl_export(expt[0], expt[1])
if cleanup:
@@ -498,19 +516,20 @@ class ArgHandler:
if not self.open:
# Create empty dir for imported database(s)
if self.gui:
dbid = config.get('database.backend')
self.imp_db_path, title = self.dbman.create_new_db_cli(
dbid=dbid)
dbid = config.get("database.backend")
self.imp_db_path, title = self.dbman.create_new_db_cli(dbid=dbid)
else:
self.imp_db_path = get_empty_tempdir("import_dbdir")
dbid = config.get('database.backend')
dbid = config.get("database.backend")
newdb = make_database(dbid)
versionpath = os.path.join(self.imp_db_path, str(DBBACKEND))
with open(versionpath, "w") as version_file:
version_file.write(dbid)
try:
self.smgr.open_activate(self.imp_db_path, self.username, self.password)
self.smgr.open_activate(
self.imp_db_path, self.username, self.password
)
msg = _("Created empty Family Tree successfully")
print(msg, file=sys.stderr)
except:
@@ -519,9 +538,10 @@ class ArgHandler:
sys.exit(1)
for imp in self.imports:
msg = _("Importing: file %(filename)s, format %(format)s."
) % {'filename' : imp[0],
'format' : imp[1]}
msg = _("Importing: file %(filename)s, format %(format)s.") % {
"filename": imp[0],
"format": imp[1],
}
print(msg, file=sys.stderr)
self.cl_import(imp[0], imp[1])
@@ -552,8 +572,10 @@ class ArgHandler:
if force_unlock:
self.dbman.break_lock(dbpath)
if self.dbman.is_locked(dbpath):
self.__error((_("Database is locked, cannot open it!") + '\n' +
_(" Info: %s")) % find_locker_name(dbpath))
self.__error(
(_("Database is locked, cannot open it!") + "\n" + _(" Info: %s"))
% find_locker_name(dbpath)
)
return False
if self.dbman.needs_recovery(dbpath):
self.__error(_("Database needs recovery, cannot open it!"))
@@ -610,10 +632,9 @@ class ArgHandler:
options_str_dict = _split_options(options_str)
except:
options_str_dict = {}
print(_("Ignoring invalid options string."),
file=sys.stderr)
print(_("Ignoring invalid options string."), file=sys.stderr)
name = options_str_dict.pop('name', None)
name = options_str_dict.pop("name", None)
_cl_list = pmgr.get_reg_reports(gui=False)
if name:
for pdata in _cl_list:
@@ -626,43 +647,51 @@ class ArgHandler:
report_class = getattr(mod, pdata.reportclass)
options_class = getattr(mod, pdata.optionclass)
if category in (CATEGORY_BOOK, CATEGORY_CODE):
options_class(self.dbstate.db, name, category,
options_str_dict)
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)
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. "
msg = _(
"Report name not given. "
"Please use one of %(donottranslate)s=reportname"
) % {'donottranslate' : '[-p|--options] name'}
) % {"donottranslate": "[-p|--options] name"}
print(_("%s\n Available names are:") % msg, file=sys.stderr)
for pdata in sorted(_cl_list, key=lambda pdata: pdata.id.lower()):
# Print cli report name ([item[0]), GUI report name (item[4])
if len(pdata.id) <= 25:
print(" %s%s- %s" % (pdata.id,
" " * (26 - len(pdata.id)),
pdata.name),
file=sys.stderr)
print(
" %s%s- %s"
% (pdata.id, " " * (26 - len(pdata.id)), pdata.name),
file=sys.stderr,
)
else:
print(" %s\t- %s" % (pdata.id, pdata.name),
file=sys.stderr)
print(" %s\t- %s" % (pdata.id, pdata.name), file=sys.stderr)
elif action == "tool":
from gramps.gui.plug import tool
try:
options_str_dict = dict([tuple(chunk.split('='))
for chunk in options_str.split(',')])
options_str_dict = dict(
[tuple(chunk.split("=")) for chunk in options_str.split(",")]
)
except:
options_str_dict = {}
print(_("Ignoring invalid options string."),
file=sys.stderr)
print(_("Ignoring invalid options string."), file=sys.stderr)
name = options_str_dict.pop('name', None)
name = options_str_dict.pop("name", None)
_cli_tool_list = pmgr.get_reg_tools(gui=False)
if name:
for pdata in _cli_tool_list:
@@ -674,53 +703,59 @@ class ArgHandler:
category = pdata.category
tool_class = getattr(mod, pdata.toolclass)
options_class = getattr(mod, pdata.optionclass)
tool.cli_tool(dbstate=self.dbstate,
tool.cli_tool(
dbstate=self.dbstate,
name=name,
category=category,
tool_class=tool_class,
options_class=options_class,
options_str_dict=options_str_dict,
user=self.user)
user=self.user,
)
return
msg = _("Unknown tool name.")
else:
msg = _("Tool name not given. "
msg = _(
"Tool name not given. "
"Please use one of %(donottranslate)s=toolname."
) % {'donottranslate' : '[-p|--options] name'}
) % {"donottranslate": "[-p|--options] name"}
print(_("%s\n Available names are:") % msg, file=sys.stderr)
for pdata in sorted(_cli_tool_list,
key=lambda pdata: pdata.id.lower()):
for pdata in sorted(_cli_tool_list, key=lambda pdata: pdata.id.lower()):
# Print cli report name ([item[0]), GUI report name (item[4])
if len(pdata.id) <= 25:
print(" %s%s- %s" % (pdata.id,
" " * (26 - len(pdata.id)),
pdata.name),
file=sys.stderr)
print(
" %s%s- %s"
% (pdata.id, " " * (26 - len(pdata.id)), pdata.name),
file=sys.stderr,
)
else:
print(" %s\t- %s" % (pdata.id, pdata.name),
file=sys.stderr)
print(" %s\t- %s" % (pdata.id, pdata.name), file=sys.stderr)
elif action == "book":
try:
options_str_dict = _split_options(options_str)
except:
options_str_dict = {}
print(_("Ignoring invalid options string."),
file=sys.stderr)
print(_("Ignoring invalid options string."), file=sys.stderr)
name = options_str_dict.pop('name', None)
book_list = BookList('books.xml', self.dbstate.db)
name = options_str_dict.pop("name", None)
book_list = BookList("books.xml", self.dbstate.db)
if name:
if name in book_list.get_book_names():
cl_book(self.dbstate.db, name, book_list.get_book(name),
options_str_dict)
cl_book(
self.dbstate.db,
name,
book_list.get_book(name),
options_str_dict,
)
return
msg = _("Unknown book name.")
else:
msg = _("Book name not given. "
msg = _(
"Book name not given. "
"Please use one of %(donottranslate)s=bookname."
) % {'donottranslate' : '[-p|--options] name'}
) % {"donottranslate": "[-p|--options] name"}
print(_("%s\n Available names are:") % msg, file=sys.stderr)
for name in sorted(book_list.get_book_names()):

View File

@@ -47,14 +47,24 @@ from glob import glob
# gramps modules
#
# -------------------------------------------------------------------------
from gramps.gen.const import (LONGOPTS, SHORTOPTS, USER_PLUGINS, VERSION_DIR,
USER_CACHE, USER_CONFIG, USER_DATA, THUMB_DIR,
USER_CSS)
from gramps.gen.const import (
LONGOPTS,
SHORTOPTS,
USER_PLUGINS,
VERSION_DIR,
USER_CACHE,
USER_CONFIG,
USER_DATA,
THUMB_DIR,
USER_CSS,
)
from gramps.gen.utils.cast import get_type_converter
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
_HELP = _("""
_HELP = _(
"""
Usage: gramps [OPTION...]
--load-modules=MODULE1,MODULE2,... Dynamic modules to load
@@ -91,9 +101,11 @@ Application options
X - Books are cleared, reports and tool settings to default
F - filters are cleared
E - Everything is set to default or cleared
""")
"""
)
_USAGE = _("""
_USAGE = _(
"""
Example of usage of Gramps command line interface
1. To import four databases (whose formats can be determined from their names)
@@ -136,7 +148,9 @@ gramps
Note: These examples are for bash shell.
Syntax may be different for other shells and for Windows.
""")
"""
)
# -------------------------------------------------------------------------
# ArgParser
@@ -241,14 +255,13 @@ class ArgParser:
Any errors are added to self.errors
"""
try:
options, leftargs = getopt.getopt(self.args[1:],
SHORTOPTS, LONGOPTS)
options, leftargs = getopt.getopt(self.args[1:], SHORTOPTS, LONGOPTS)
except getopt.GetoptError as getopt_error:
self.errors.append(
self.construct_error(
"Type gramps --help for an overview of "
"commands, or read the manual pages.",
error=getopt_error
error=getopt_error,
)
)
@@ -257,7 +270,7 @@ class ArgParser:
# Some args can work on a list of databases:
if leftargs:
for option, value in options:
if option in ['-L', '-l', '-t']:
if option in ["-L", "-l", "-t"]:
self.database_names = leftargs
leftargs = []
@@ -265,12 +278,10 @@ class ArgParser:
# 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],
file=sys.stderr)
print(_("Trying to open: %s ...") % leftargs[0], file=sys.stderr)
# see if force open is on
for option, value in options:
if option in ('-u', '--force-unlock'):
if option in ("-u", "--force-unlock"):
self.force_unlock = True
break
return
@@ -279,77 +290,81 @@ class ArgParser:
cleandbg = []
need_to_quit = False
for opt_ix, (option, value) in enumerate(options):
if option in ['-O', '--open']:
if option in ["-O", "--open"]:
self.open = value
elif option in ['-C', '--create']:
elif option in ["-C", "--create"]:
self.create = value
elif option in ['-U', '--username']:
elif option in ["-U", "--username"]:
self.username = value
elif option in ['-P', '--password']:
elif option in ["-P", "--password"]:
self.password = value
elif option in ['-i', '--import']:
elif option in ["-i", "--import"]:
family_tree_format = None
if (opt_ix < len(options) - 1
and options[opt_ix + 1][0] in ('-f', '--format')):
if opt_ix < len(options) - 1 and options[opt_ix + 1][0] in (
"-f",
"--format",
):
family_tree_format = options[opt_ix + 1][1]
self.imports.append((value, family_tree_format))
elif option in ['-r', '--remove']:
elif option in ["-r", "--remove"]:
self.removes.append(value)
elif option in ['-e', '--export']:
elif option in ["-e", "--export"]:
family_tree_format = None
if (opt_ix < len(options) - 1
and options[opt_ix + 1][0] in ('-f', '--format')):
if opt_ix < len(options) - 1 and options[opt_ix + 1][0] in (
"-f",
"--format",
):
family_tree_format = options[opt_ix + 1][1]
abs_name = os.path.abspath(os.path.expanduser(value))
if not os.path.exists(abs_name):
# The file doesn't exists, try to create it.
try:
open(abs_name, 'w').close()
open(abs_name, "w").close()
os.unlink(abs_name)
except OSError as e:
message = _("WARNING: %(strerr)s "
message = _(
"WARNING: %(strerr)s "
"(errno=%(errno)s):\n"
"WARNING: %(name)s\n") % {
'strerr' : e.strerror,
'errno' : e.errno,
'name' : e.filename}
"WARNING: %(name)s\n"
) % {"strerr": e.strerror, "errno": e.errno, "name": e.filename}
print(message)
sys.exit(1)
self.exports.append((value, family_tree_format))
elif option in ['-a', '--action']:
elif option in ["-a", "--action"]:
action = value
if action not in ('report', 'tool', 'book'):
print(_("Unknown action: %s. Ignoring."
) % action,
file=sys.stderr)
if action not in ("report", "tool", "book"):
print(_("Unknown action: %s. Ignoring.") % action, file=sys.stderr)
continue
options_str = ""
if (opt_ix < len(options)-1
and options[opt_ix+1][0] in ('-p', '--options')):
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']:
print(_('setup debugging'), value, file=sys.stderr)
elif option in ["-d", "--debug"]:
print(_("setup debugging"), value, file=sys.stderr)
logger = logging.getLogger(value)
logger.setLevel(logging.DEBUG)
cleandbg += [opt_ix]
elif option in ['-l']:
elif option in ["-l"]:
self.list = True
elif option in ['-L']:
elif option in ["-L"]:
self.list_more = True
elif option in ['-t']:
elif option in ["-t"]:
self.list_table = True
elif option in ['-s', '--show']:
elif option in ["-s", "--show"]:
from gramps.gen.config import config
print(_("Gramps config settings from %s:"
) % config.filename)
print(_("Gramps config settings from %s:") % config.filename)
for sect, settings in config.data.items():
for settings_index, setting in settings.items():
print("%s.%s=%s" % (sect, settings_index, repr(value)))
print()
sys.exit(0)
elif option in ['-c', '--config']:
elif option in ["-c", "--config"]:
from gramps.gen.config import config
cfg_name = value
set_value = False
if cfg_name:
@@ -358,11 +373,11 @@ class ArgParser:
set_value = True
if config.has_default(cfg_name):
setting_value = config.get(cfg_name)
print(_("Current Gramps config setting: "
"%(name)s:%(value)s"
) % {'name' : cfg_name,
'value' : repr(setting_value)},
file=sys.stderr)
print(
_("Current Gramps config setting: " "%(name)s:%(value)s")
% {"name": cfg_name, "value": repr(setting_value)},
file=sys.stderr,
)
if set_value:
# does a user want the default config value?
if new_value in ("DEFAULT", _("DEFAULT")):
@@ -372,64 +387,74 @@ class ArgParser:
new_value = converter(new_value)
config.set(cfg_name, new_value)
# Translators: indent "New" to match "Current"
print(_(" New Gramps config setting: "
print(
_(
" New Gramps config setting: "
"%(name)s:%(value)s"
) % {'name' : cfg_name,
'value' : repr(config.get(cfg_name))},
file=sys.stderr)
)
% {
"name": cfg_name,
"value": repr(config.get(cfg_name)),
},
file=sys.stderr,
)
else:
need_to_quit = True
else:
print(_("Gramps: no such config setting: '%s'"
) % cfg_name,
file=sys.stderr)
print(
_("Gramps: no such config setting: '%s'") % cfg_name,
file=sys.stderr,
)
need_to_quit = True
cleandbg += [opt_ix]
elif option in ['-h', '-?', '--help']:
elif option in ["-h", "-?", "--help"]:
self.help = True
elif option in ['-u', '--force-unlock']:
elif option in ["-u", "--force-unlock"]:
self.force_unlock = True
elif option in ['--usage']:
elif option in ["--usage"]:
self.usage = True
elif option in ['-y', '--yes']:
elif option in ["-y", "--yes"]:
self.auto_accept = True
elif option in ['-q', '--quiet']:
elif option in ["-q", "--quiet"]:
self.quiet = True
elif option in ['-S', '--safe']:
elif option in ["-S", "--safe"]:
cleandbg += [opt_ix]
elif option in ['-D', '--default']:
elif option in ["-D", "--default"]:
def rmtree(path):
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
if 'E' in value or 'A' in value: # clear addons
if "E" in value or "A" in value: # clear addons
rmtree(USER_PLUGINS)
if 'E' in value or 'P' in value: # clear ini preferences
if "E" in value or "P" in value: # clear ini preferences
for fil in glob(os.path.join(VERSION_DIR, "*.*")):
if "custom_filters.xml" in fil:
continue
os.remove(fil)
# create gramps.ini so config won't load the one from an
# older version of Gramps.
with open(os.path.join(VERSION_DIR, 'gramps.ini'), 'w'):
with open(os.path.join(VERSION_DIR, "gramps.ini"), "w"):
pass
if 'E' in value or 'F' in value: # clear filters
if "E" in value or "F" in value: # clear filters
fil = os.path.join(VERSION_DIR, "custom_filters.xml")
if os.path.isfile(fil):
os.remove(fil)
if 'E' in value or 'X' in value: # clear xml reports/tools
if "E" in value or "X" in value: # clear xml reports/tools
for fil in glob(os.path.join(USER_DATA, "*.xml")):
os.remove(fil)
if 'E' in value or 'Z' in value: # clear upgrade zips
if "E" in value or "Z" in value: # clear upgrade zips
for fil in glob(os.path.join(USER_DATA, "*.zip")):
os.remove(fil)
if 'E' in value: # Everything else
if "E" in value: # Everything else
rmtree(THUMB_DIR)
rmtree(USER_CSS)
rmtree(os.path.join(USER_CACHE, "maps"))
for fil in (glob(os.path.join(USER_CACHE, "*"))
for fil in (
glob(os.path.join(USER_CACHE, "*"))
+ glob(os.path.join(USER_CONFIG, "*"))
+ glob(os.path.join(USER_DATA, "*"))):
+ glob(os.path.join(USER_DATA, "*"))
):
if os.path.isfile(fil):
os.remove(fil)
sys.exit(0) # Done with Default
@@ -439,14 +464,13 @@ class ArgParser:
for ind in cleandbg:
del options[ind]
if (len(options) > 0
if (
len(options) > 0
and self.open is None
and self.imports == []
and self.removes == []
and not (self.list
or self.list_more
or self.list_table
or self.help)):
and not (self.list or self.list_more or self.list_table or self.help)
):
self.errors.append(
self.construct_error(
"To use in the command-line mode, supply at "
@@ -469,10 +493,9 @@ class ArgParser:
translated_message = _(error_message + suggestion_message) % cli_args
if error:
translated_message = str(error) + '\n' + translated_message
return _('Error parsing the arguments'), translated_message
translated_message = str(error) + "\n" + translated_message
return _("Error parsing the arguments"), translated_message
# -------------------------------------------------------------------------
# Determine the need for GUI

View File

@@ -51,6 +51,7 @@ from gramps.gen.constfunc import win
from gramps.gen.db.dbconst import DBLOGNAME, DBBACKEND
from gramps.gen.db.utils import make_database, get_dbid_from_path
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -69,7 +70,8 @@ _LOG = logging.getLogger(DBLOGNAME)
DEFAULT_TITLE = _("Family Tree")
NAME_FILE = "name.txt"
META_NAME = "meta_data.db"
UNAVAILABLE = _('Unavailable')
UNAVAILABLE = _("Unavailable")
# -------------------------------------------------------------------------
#
@@ -80,11 +82,13 @@ def _errordialog(title, errormessage):
"""
Show the error. A title for the error and an errormessage
"""
print(_('ERROR: %(title)s \n %(message)s') % {
'title': title,
'message': errormessage})
print(
_("ERROR: %(title)s \n %(message)s")
% {"title": title, "message": errormessage}
)
sys.exit()
# -------------------------------------------------------------------------
#
# CLIDbManager
@@ -95,6 +99,7 @@ class CLIDbManager:
Database manager without GTK functionality, allows users to create and
open databases
"""
IND_NAME = 0
IND_PATH = 1
IND_PATH_NAMEFILE = 2
@@ -116,6 +121,7 @@ class CLIDbManager:
}
ERROR = _errordialog
def __init__(self, dbstate):
self.dbstate = dbstate
self.msg = None
@@ -166,12 +172,15 @@ class CLIDbManager:
retval = {_("Unavailable"): str(msg)[:74] + "..."}
else:
retval = {_("Unavailable"): "locked"}
retval.update({_("Family Tree"): name,
retval.update(
{
_("Family Tree"): name,
_("Path"): dirpath,
_("Database"): self.get_backend_name_from_dbid(dbid),
_("Last accessed"): time_val(dirpath)[1],
_("Locked?"): self.is_locked(dirpath),
})
}
)
return retval
def get_backend_name_from_dbid(self, dbid):
@@ -185,22 +194,34 @@ class CLIDbManager:
"""
Prints a detailed list of the known family trees.
"""
print(_('Gramps Family Trees:'))
print(_("Gramps Family Trees:"))
for item in self.current_names:
(name, dirpath, path_name, last,
tval, enable, stock_id, backend_type) = item
if (database_names is None or
any([(re.match("^" + dbname + "$", name) or
dbname == name)
for dbname in database_names])):
(
name,
dirpath,
path_name,
last,
tval,
enable,
stock_id,
backend_type,
) = item
if database_names is None or any(
[
(re.match("^" + dbname + "$", name) or dbname == name)
for dbname in database_names
]
):
summary = self.get_dbdir_summary(dirpath, name)
print(_('Family Tree "%s":') % summary[_("Family Tree")])
for item in sorted(summary):
if item != "Family Tree":
# Translators: needed for French, ignore otherwise
print(' ' + _("%(str1)s: %(str2)s"
) % {'str1' : item,
'str2' : summary[item]})
print(
" "
+ _("%(str1)s: %(str2)s")
% {"str1": item, "str2": summary[item]}
)
def family_tree_summary(self, database_names=None):
"""
@@ -209,12 +230,22 @@ class CLIDbManager:
# make the default directory if it does not exist
summary_list = []
for item in self.current_names:
(name, dirpath, path_name, last,
tval, enable, stock_id, backend_type) = item
if (database_names is None or
any([(re.match("^" + dbname + "$", name) or
dbname == name)
for dbname in database_names])):
(
name,
dirpath,
path_name,
last,
tval,
enable,
stock_id,
backend_type,
) = item
if database_names is None or any(
[
(re.match("^" + dbname + "$", name) or dbname == name)
for dbname in database_names
]
):
retval = self.get_dbdir_summary(dirpath, name)
summary_list.append(retval)
return summary_list
@@ -224,7 +255,7 @@ class CLIDbManager:
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('database.path'))
dbdir = os.path.expanduser(config.get("database.path"))
db_ok = make_dbdir(dbdir)
self.current_names = []
@@ -234,19 +265,29 @@ class CLIDbManager:
path_name = os.path.join(dirpath, NAME_FILE)
backend_type = get_dbid_from_path(dirpath)
if os.path.isfile(path_name):
with open(path_name, 'r', encoding='utf8') as file:
with open(path_name, "r", encoding="utf8") as file:
name = file.readline().strip()
(tval, last) = time_val(dirpath)
(enable, stock_id) = self.icon_values(
dirpath, self.active, self.dbstate.is_open())
dirpath, self.active, self.dbstate.is_open()
)
if stock_id == 'gramps-lock':
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, backend_type))
(
name,
os.path.join(dbdir, dpath),
path_name,
last,
tval,
enable,
stock_id,
backend_type,
)
)
self.current_names.sort()
@@ -271,13 +312,13 @@ class CLIDbManager:
"""
Do needed things to start import visually, eg busy cursor
"""
print(_('Starting Import, %s') % msg)
print(_("Starting Import, %s") % msg)
def __end_cursor(self):
"""
Set end of a busy cursor
"""
print(_('Import finished...'))
print(_("Import finished..."))
def create_new_db_cli(self, title=None, create_db=True, dbid=None):
"""
@@ -292,22 +333,23 @@ class CLIDbManager:
name_list = [name[0] for name in self.current_names]
title = find_next_db_name(name_list)
with open(path_name, "w", encoding='utf8') as name_file:
with open(path_name, "w", encoding="utf8") as name_file:
name_file.write(title)
if create_db:
if dbid is None:
dbid = config.get('database.backend')
dbid = config.get("database.backend")
newdb = make_database(dbid)
backend_path = os.path.join(new_path, DBBACKEND)
with open(backend_path, "w", encoding='utf8') as backend_file:
with open(backend_path, "w", encoding="utf8") as backend_file:
backend_file.write(dbid)
(tval, last) = time_val(new_path)
self.current_names.append((title, new_path, path_name,
last, tval, False, "", dbid))
self.current_names.append(
(title, new_path, path_name, last, tval, False, "", dbid)
)
return new_path, title
def _create_new_db(self, title=None, dbid=None, edit_entry=False):
@@ -357,10 +399,8 @@ class CLIDbManager:
for plugin in pmgr.get_import_plugins():
if format == plugin.get_extension():
dbid = config.get('database.backend')
new_path, name = self._create_new_db(name, dbid=dbid,
edit_entry=False)
dbid = config.get("database.backend")
new_path, name = self._create_new_db(name, dbid=dbid, edit_entry=False)
# Create a new database
self.__start_cursor(_("Importing data..."))
@@ -406,35 +446,37 @@ class CLIDbManager:
Deletes a database folder given a pattenr that matches
its proper name.
"""
dbdir = os.path.expanduser(config.get('database.path'))
dbdir = os.path.expanduser(config.get("database.path"))
match_list = []
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):
with open(path_name, 'r', encoding='utf8') as file:
with open(path_name, "r", encoding="utf8") as file:
name = file.readline().strip()
if re.match("^" + dbname + "$", name) or dbname == name:
match_list.append((name, dirpath))
if len(match_list) == 0:
CLIDbManager.ERROR("Family tree not found",
"No matching family tree found: '%s'" % dbname)
CLIDbManager.ERROR(
"Family tree not found", "No matching family tree found: '%s'" % dbname
)
# now delete them:
for (name, directory) in match_list:
for name, directory in match_list:
if user is None or user.prompt(
_('Remove family tree warning'),
_('Are you sure you want to remove '
'the family tree named\n"%s"?'
) % name,
_('yes'), _('no'), default_label=_('no')):
_("Remove family tree warning"),
_("Are you sure you want to remove " 'the family tree named\n"%s"?')
% name,
_("yes"),
_("no"),
default_label=_("no"),
):
try:
for (top, dirs, files) in os.walk(directory):
for top, dirs, files in os.walk(directory):
for filename in files:
os.unlink(os.path.join(top, filename))
os.rmdir(directory)
except (IOError, OSError) as msg:
CLIDbManager.ERROR(_("Could not delete Family Tree"),
str(msg))
CLIDbManager.ERROR(_("Could not delete Family Tree"), str(msg))
def rename_database(self, filepath, new_text):
"""
@@ -442,9 +484,9 @@ class CLIDbManager:
Returns old_name, new_name if success, None, None if no success
"""
try:
with open(filepath, "r", encoding='utf8') as name_file:
with open(filepath, "r", encoding="utf8") as name_file:
old_text = name_file.read()
with open(filepath, "w", encoding='utf8') as name_file:
with open(filepath, "w", encoding="utf8") as name_file:
name_file.write(new_text)
except (OSError, IOError) as msg:
CLIDbManager.ERROR(_("Could not rename Family Tree"), str(msg))
@@ -472,6 +514,7 @@ class CLIDbManager:
else:
return (False, self.ICON_MAP[self.ICON_NONE])
def make_dbdir(dbdir):
"""
Create the default database directory, as defined by dbdir
@@ -480,13 +523,18 @@ def make_dbdir(dbdir):
if not os.path.isdir(dbdir):
os.makedirs(dbdir)
except (IOError, OSError) as msg:
LOG.error(_("\nERROR: Wrong database path in Edit Menu->Preferences.\n"
LOG.error(
_(
"\nERROR: Wrong database path in Edit Menu->Preferences.\n"
"Open preferences and set correct database path.\n\n"
"Details: Could not make database directory:\n %s\n\n"),
str(msg))
"Details: Could not make database directory:\n %s\n\n"
),
str(msg),
)
return False
return True
def find_next_db_name(name_list):
"""
Scan the name list, looking for names that do not yet exist.
@@ -499,6 +547,7 @@ def find_next_db_name(name_list):
return title
i += 1
def find_next_db_dir():
"""
Searches the default directory for the first available default
@@ -507,12 +556,13 @@ def find_next_db_dir():
"""
while True:
base = "%x" % int(time.time())
dbdir = os.path.expanduser(config.get('database.path'))
dbdir = os.path.expanduser(config.get("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
@@ -529,12 +579,13 @@ def time_val(dirpath):
tval_mod = os.stat(meta)[8]
if tval_mod > tval:
tval = tval_mod
last = time.strftime('%x %X', time.localtime(tval))
last = time.strftime("%x %X", time.localtime(tval))
else:
tval = 0
last = _("Never")
return (tval, last)
def find_locker_name(dirpath):
"""
Opens the lock file if it exists, reads the contexts which is "USERNAME"
@@ -545,7 +596,7 @@ def find_locker_name(dirpath):
"""
try:
fname = os.path.join(dirpath, "lock")
with open(fname, 'r', encoding='utf8') as ifile:
with open(fname, "r", encoding="utf8") as ifile:
username = ifile.read().strip()
# feature request 2356: avoid genitive form
last = _("Locked by %s") % username

View File

@@ -32,6 +32,7 @@ Provides also two small base classes: :class:`CLIDbLoader`, :class:`CLIManager`
#
# -------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
import os
import sys
@@ -51,16 +52,19 @@ from gramps.gen.db.dbconst import DBBACKEND
from gramps.gen.db.utils import make_database
from gramps.gen.errors import DbError
from gramps.gen.dbstate import DbState
from gramps.gen.db.exceptions import (DbUpgradeRequiredError,
from gramps.gen.db.exceptions import (
DbUpgradeRequiredError,
DbSupportedError,
DbVersionError,
DbPythonError,
DbConnectionError)
DbConnectionError,
)
from gramps.gen.plug import BasePluginManager
from gramps.gen.utils.config import get_researcher
from gramps.gen.recentfiles import recent_files
from gramps.gen.filters import reload_custom_filters
# -------------------------------------------------------------------------
#
# CLI DbLoader class
@@ -71,6 +75,7 @@ class CLIDbLoader:
Base class for Db loading action inside a :class:`.DbState`. Only the
minimum is present needed for CLI handling
"""
def __init__(self, dbstate):
self.dbstate = dbstate
@@ -78,14 +83,14 @@ class CLIDbLoader:
"""
Issue a warning message. Inherit for GUI action
"""
print(_('WARNING: %s') % warnmessage, file=sys.stderr)
print(_("WARNING: %s") % warnmessage, file=sys.stderr)
def _errordialog(self, title, errormessage):
"""
Show the error. A title for the error and an errormessage
Inherit for GUI action
"""
print(_('ERROR: %s') % errormessage, file=sys.stderr)
print(_("ERROR: %s") % errormessage, file=sys.stderr)
sys.exit(1)
def _dberrordialog(self, msg):
@@ -98,14 +103,18 @@ class CLIDbLoader:
.. note:: Inherit for GUI action
"""
self._errordialog(
'',
"",
_("Low level database corruption detected")
+ '\n' +
_("Gramps has detected a problem in the underlying "
+ "\n"
+ _(
"Gramps has detected a problem in the underlying "
"database. This can sometimes be repaired from "
"the Family Tree Manager. Select the database and "
'click on the Repair button'
) + '\n\n' + str(msg))
"click on the Repair button"
)
+ "\n\n"
+ str(msg),
)
def _begin_progress(self):
"""
@@ -146,13 +155,14 @@ class CLIDbLoader:
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.'))
self._warn(
_("Read only database"),
_("You do not have write access " "to the selected file."),
)
else:
mode = "w"
else:
mode = 'w'
mode = "w"
dbid_path = os.path.join(filename, DBBACKEND)
if os.path.isfile(dbid_path):
@@ -169,16 +179,26 @@ class CLIDbLoader:
self._begin_progress()
try:
self.dbstate.db.load(filename, self._pulse_progress, mode,
username=username, password=password)
except (DbConnectionError, DbSupportedError, DbUpgradeRequiredError,
DbVersionError, DbPythonError, DbConnectionError) as msg:
self.dbstate.db.load(
filename,
self._pulse_progress,
mode,
username=username,
password=password,
)
except (
DbConnectionError,
DbSupportedError,
DbUpgradeRequiredError,
DbVersionError,
DbPythonError,
DbConnectionError,
) as msg:
self.dbstate.no_database()
self._errordialog(_("Cannot open database"), str(msg))
except OSError as msg:
self.dbstate.no_database()
self._errordialog(
_("Could not open file: %s") % filename, str(msg))
self._errordialog(_("Could not open file: %s") % filename, str(msg))
except DbError as msg:
self.dbstate.no_database()
self._dberrordialog(msg)
@@ -187,12 +207,14 @@ class CLIDbLoader:
LOG.error("Failed to open database.", exc_info=True)
return True
# -------------------------------------------------------------------------
#
# CLIManager class
#
# -------------------------------------------------------------------------
class CLIManager:
"""
Sessionmanager for Gramps.
@@ -201,6 +223,7 @@ class CLIManager:
Aim is to manage a dbstate on which to work (load, unload), and interact
with the plugin session
"""
def __init__(self, dbstate, setloader, user):
self.dbstate = dbstate
if setloader:
@@ -221,7 +244,7 @@ class CLIManager:
"""
Show the error. A title for the error and an errormessage
"""
print(_('ERROR: %s') % errormessage, file=sys.stderr)
print(_("ERROR: %s") % errormessage, file=sys.stderr)
sys.exit(1)
def _read_recent_file(self, filename, username=None, password=None):
@@ -235,26 +258,30 @@ class CLIManager:
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."))
_("Family Tree does not exist, as it has been deleted."),
)
return
if os.path.isfile(os.path.join(filename, "lock")):
self._errordialog(
_("The database is locked."),
_("Use the --force-unlock option if you are sure "
"that the database is not in use."))
_(
"Use the --force-unlock option if you are sure "
"that the database is not in use."
),
)
return
if self.db_loader.read_file(filename, username, password):
# Attempt to figure out the database title
path = os.path.join(filename, "name.txt")
try:
with open(path, encoding='utf8') as ifile:
with open(path, encoding="utf8") as ifile:
title = ifile.readline().strip()
except:
title = filename
self._post_load_newdb(filename, 'x-directory/normal', title)
self._post_load_newdb(filename, "x-directory/normal", title)
def _post_load_newdb(self, filename, filetype, title=None):
"""
@@ -283,20 +310,18 @@ class CLIManager:
# If the DB Owner Info is empty and
# [default] Researcher is not empty and
# database is empty, then copy default researcher to DB owner
if (res.is_empty()
and not owner.is_empty()
and self.dbstate.db.get_total() == 0):
if res.is_empty() and not owner.is_empty() and self.dbstate.db.get_total() == 0:
self.dbstate.db.set_researcher(owner)
name_displayer.clear_custom_formats()
name_displayer.set_name_format(self.dbstate.db.name_formats)
fmt_default = config.get('preferences.name-format')
fmt_default = config.get("preferences.name-format")
name_displayer.set_default_format(fmt_default)
self.dbstate.db.enable_signals()
self.dbstate.signal_change()
config.set('paths.recent-file', filename)
config.set("paths.recent-file", filename)
recent_files(filename, name)
self.file_loaded = True
@@ -310,6 +335,7 @@ class CLIManager:
if rescan: # supports updated plugin installs
self._pmgr.reload_plugins()
def startcli(errors, argparser):
"""
Starts a cli session of Gramps.
@@ -319,17 +345,16 @@ def startcli(errors, argparser):
"""
if errors:
# already errors encountered. Show first one on terminal and exit
errmsg = _('Error encountered: %s') % errors[0][0]
errmsg = _("Error encountered: %s") % errors[0][0]
print(errmsg, file=sys.stderr)
errmsg = _(' Details: %s') % errors[0][1]
errmsg = _(" Details: %s") % errors[0][1]
print(errmsg, file=sys.stderr)
sys.exit(1)
if argparser.errors:
errmsg = _('Error encountered in argument parsing: %s'
) % argparser.errors[0][0]
errmsg = _("Error encountered in argument parsing: %s") % argparser.errors[0][0]
print(errmsg, file=sys.stderr)
errmsg = _(' Details: %s') % argparser.errors[0][1]
errmsg = _(" Details: %s") % argparser.errors[0][1]
print(errmsg, file=sys.stderr)
sys.exit(1)
@@ -338,6 +363,7 @@ def startcli(errors, argparser):
# we need a manager for the CLI session
from .user import User
user = User(auto_accept=argparser.auto_accept, quiet=argparser.quiet)
climanager = CLIManager(dbstate, True, user)
@@ -346,6 +372,7 @@ def startcli(errors, argparser):
reload_custom_filters()
# handle the arguments
from .arghandler import ArgHandler
handler = ArgHandler(dbstate, argparser, climanager)
# create a manager to manage the database

View File

@@ -38,6 +38,7 @@ import os
import sys
import logging
LOG = logging.getLogger(".")
# -------------------------------------------------------------------------
@@ -46,27 +47,51 @@ LOG = logging.getLogger(".")
#
# -------------------------------------------------------------------------
from gramps.gen.plug import BasePluginManager
from gramps.gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle,
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc,
treedoc)
from gramps.gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
MediaOption, PersonListOption, NumberOption,
BooleanOption, DestinationOption, Option,
TextOption, EnumeratedListOption,
StringOption)
from gramps.gen.plug.docgen import (
StyleSheet,
StyleSheetList,
PaperStyle,
PAPER_PORTRAIT,
PAPER_LANDSCAPE,
graphdoc,
treedoc,
)
from gramps.gen.plug.menu import (
FamilyOption,
PersonOption,
NoteOption,
MediaOption,
PersonListOption,
NumberOption,
BooleanOption,
DestinationOption,
Option,
TextOption,
EnumeratedListOption,
StringOption,
)
from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.errors import ReportError, FilterError
from gramps.gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
CATEGORY_GRAPHVIZ, CATEGORY_TREE,
CATEGORY_CODE, ReportOptions, append_styles)
from gramps.gen.plug.report import (
CATEGORY_TEXT,
CATEGORY_DRAW,
CATEGORY_BOOK,
CATEGORY_GRAPHVIZ,
CATEGORY_TREE,
CATEGORY_CODE,
ReportOptions,
append_styles,
)
from gramps.gen.plug.report._paper import paper_sizes
from gramps.gen.const import USER_HOME, DOCGEN_OPTIONS
from gramps.gen.dbstate import DbState
from ..grampscli import CLIManager
from ..user import User
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# ------------------------------------------------------------------------
#
# Private Functions
@@ -80,8 +105,9 @@ def _convert_str_to_match_type(str_val, type_val):
ret_type = type(type_val)
if isinstance(type_val, str):
if ((str_val.startswith("'") and str_val.endswith("'"))
or (str_val.startswith('"') and str_val.endswith('"'))):
if (str_val.startswith("'") and str_val.endswith("'")) or (
str_val.startswith('"') and str_val.endswith('"')
):
# Remove enclosing quotes
return str(str_val[1:-1])
else:
@@ -141,6 +167,7 @@ def _convert_str_to_match_type(str_val, type_val):
return ret_val
def _validate_options(options, dbase):
"""
Validate all options by making sure that their values are consistent with
@@ -168,8 +195,7 @@ def _validate_options(options, dbase):
phandle = None
person = dbase.get_person_from_handle(phandle)
if not person:
print(_("ERROR: Please specify a person"),
file=sys.stderr)
print(_("ERROR: Please specify a person"), file=sys.stderr)
if person:
option.set_value(person.get_gramps_id())
@@ -195,6 +221,7 @@ def _validate_options(options, dbase):
else:
print(_("ERROR: Please specify a family"), file=sys.stderr)
# ------------------------------------------------------------------------
#
# Command-line report
@@ -205,9 +232,9 @@ class CommandLineReport:
Provide a way to generate report from the command line.
"""
def __init__(self, database, name, category, option_class, options_str_dict,
noopt=False):
def __init__(
self, database, name, category, option_class, options_str_dict, noopt=False
):
pmgr = BasePluginManager.get_instance()
self.__textdoc_plugins = []
self.__drawdoc_plugins = []
@@ -217,9 +244,11 @@ class CommandLineReport:
self.__textdoc_plugins.append(plugin)
if plugin.get_draw_support() and plugin.get_extension():
self.__drawdoc_plugins.append(plugin)
if (plugin.get_extension()
if (
plugin.get_extension()
and plugin.get_text_support()
and plugin.get_draw_support()):
and plugin.get_draw_support()
):
self.__bookdoc_plugins.append(plugin)
self.database = database
@@ -249,8 +278,9 @@ class CommandLineReport:
self.__gvoptions.add_menu_options(menu)
for name in menu.get_all_option_names():
if name not in self.option_class.options_dict:
self.option_class.options_dict[
name] = menu.get_option_by_name(name).get_value()
self.option_class.options_dict[name] = menu.get_option_by_name(
name
).get_value()
if category == CATEGORY_TREE:
# Need to include Genealogy Tree options
self.__toptions = treedoc.TreeOptions()
@@ -258,11 +288,12 @@ class CommandLineReport:
self.__toptions.add_menu_options(menu)
for name in menu.get_all_option_names():
if name not in self.option_class.options_dict:
self.option_class.options_dict[
name] = menu.get_option_by_name(name).get_value()
self.option_class.options_dict[name] = menu.get_option_by_name(
name
).get_value()
self.option_class.load_previous_values()
_validate_options(self.option_class, database)
self.show = options_str_dict.pop('show', None)
self.show = options_str_dict.pop("show", None)
self.options_str_dict = options_str_dict
self.init_standard_options(noopt)
@@ -276,76 +307,76 @@ class CommandLineReport:
Initialize the options that are hard-coded into the report system.
"""
self.options_dict = {
'of' : self.option_class.handler.module_name,
'off' : self.option_class.handler.get_format_name(),
'style' :
self.option_class.handler.get_default_stylesheet_name(),
'papers' : self.option_class.handler.get_paper_name(),
'papero' : self.option_class.handler.get_orientation(),
'paperml' : self.option_class.handler.get_margins()[0],
'papermr' : self.option_class.handler.get_margins()[1],
'papermt' : self.option_class.handler.get_margins()[2],
'papermb' : self.option_class.handler.get_margins()[3],
'css' : self.option_class.handler.get_css_filename(),
"of": self.option_class.handler.module_name,
"off": self.option_class.handler.get_format_name(),
"style": self.option_class.handler.get_default_stylesheet_name(),
"papers": self.option_class.handler.get_paper_name(),
"papero": self.option_class.handler.get_orientation(),
"paperml": self.option_class.handler.get_margins()[0],
"papermr": self.option_class.handler.get_margins()[1],
"papermt": self.option_class.handler.get_margins()[2],
"papermb": self.option_class.handler.get_margins()[3],
"css": self.option_class.handler.get_css_filename(),
}
self.options_help = {
'of' : [_("=filename"),
_("Output file name. MANDATORY"), ""],
'off' : [_("=format"), _("Output file format."), []],
'style' : [_("=name"), _("Style name."), ""],
'papers' : [_("=name"), _("Paper size name."), ""],
'papero' : [_("=number"), _("Paper orientation number."), ""],
'paperml' : [_("=number"),
_("Left paper margin"), _("Size in cm")],
'papermr' : [_("=number"),
_("Right paper margin"), _("Size in cm")],
'papermt' : [_("=number"),
_("Top paper margin"), _("Size in cm")],
'papermb' : [_("=number"),
_("Bottom paper margin"), _("Size in cm")],
'css' : [_("=css filename"),
_("CSS filename to use, html format only"), ""],
"of": [_("=filename"), _("Output file name. MANDATORY"), ""],
"off": [_("=format"), _("Output file format."), []],
"style": [_("=name"), _("Style name."), ""],
"papers": [_("=name"), _("Paper size name."), ""],
"papero": [_("=number"), _("Paper orientation number."), ""],
"paperml": [_("=number"), _("Left paper margin"), _("Size in cm")],
"papermr": [_("=number"), _("Right paper margin"), _("Size in cm")],
"papermt": [_("=number"), _("Top paper margin"), _("Size in cm")],
"papermb": [_("=number"), _("Bottom paper margin"), _("Size in cm")],
"css": [_("=css filename"), _("CSS filename to use, html format only"), ""],
}
if noopt:
return
self.options_help['of'][2] = os.path.join(USER_HOME,
"whatever_name")
self.options_help["of"][2] = os.path.join(USER_HOME, "whatever_name")
if self.category == CATEGORY_TEXT:
for plugin in self.__textdoc_plugins:
self.options_help['off'][2].append(
plugin.get_extension() + "\t" + plugin.get_description())
self.options_help["off"][2].append(
plugin.get_extension() + "\t" + plugin.get_description()
)
elif self.category == CATEGORY_DRAW:
for plugin in self.__drawdoc_plugins:
self.options_help['off'][2].append(
plugin.get_extension() + "\t" + plugin.get_description())
self.options_help["off"][2].append(
plugin.get_extension() + "\t" + plugin.get_description()
)
elif self.category == CATEGORY_BOOK:
for plugin in self.__bookdoc_plugins:
self.options_help['off'][2].append(
plugin.get_extension() + "\t" + plugin.get_description())
self.options_help["off"][2].append(
plugin.get_extension() + "\t" + plugin.get_description()
)
elif self.category == CATEGORY_GRAPHVIZ:
for graph_format in graphdoc.FORMATS:
self.options_help['off'][2].append(
graph_format["type"] + "\t" + graph_format["descr"])
self.options_help["off"][2].append(
graph_format["type"] + "\t" + graph_format["descr"]
)
elif self.category == CATEGORY_TREE:
for tree_format in treedoc.FORMATS:
self.options_help['off'][2].append(
tree_format["type"] + "\t" + tree_format["descr"])
self.options_help["off"][2].append(
tree_format["type"] + "\t" + tree_format["descr"]
)
else:
self.options_help['off'][2] = "NA"
self.options_help["off"][2] = "NA"
self.options_help['papers'][2] = [
paper.get_name() for paper in paper_sizes
if paper.get_name() != 'Custom Size']
self.options_help["papers"][2] = [
paper.get_name()
for paper in paper_sizes
if paper.get_name() != "Custom Size"
]
self.options_help['papero'][2] = ["%d\tPortrait" % PAPER_PORTRAIT,
"%d\tLandscape" % PAPER_LANDSCAPE]
self.options_help["papero"][2] = [
"%d\tPortrait" % PAPER_PORTRAIT,
"%d\tLandscape" % PAPER_LANDSCAPE,
]
self.options_help['css'][2] = os.path.join(USER_HOME,
"whatever_name.css")
self.options_help["css"][2] = os.path.join(USER_HOME, "whatever_name.css")
if self.category in (CATEGORY_TEXT, CATEGORY_DRAW):
default_style = StyleSheet()
@@ -355,7 +386,7 @@ class CommandLineReport:
style_file = self.option_class.handler.get_stylesheet_savefile()
self.style_list = StyleSheetList(style_file, default_style)
self.options_help['style'][2] = self.style_list.get_style_names()
self.options_help["style"][2] = self.style_list.get_style_names()
def init_report_options(self):
"""
@@ -365,10 +396,9 @@ class CommandLineReport:
if self.category == CATEGORY_BOOK: # a Book Report has no "menu"
for key in self.option_class.options_dict:
self.options_dict[key] = self.option_class.options_dict[key]
self.options_help[
key] = self.option_class.options_help[key][:3]
self.options_help[key] = self.option_class.options_help[key][:3]
# a Book Report can't have HTML output so "css" is meaningless
self.options_dict.pop('css')
self.options_dict.pop("css")
if not hasattr(self.option_class, "menu"):
return
@@ -393,9 +423,10 @@ class CommandLineReport:
id_list = []
for person_handle in self.database.get_person_handles(True):
person = self.database.get_person_from_handle(person_handle)
id_list.append("%s\t%s"
% (person.get_gramps_id(),
name_displayer.display(person)))
id_list.append(
"%s\t%s"
% (person.get_gramps_id(), name_displayer.display(person))
)
self.options_help[name].append(id_list)
elif isinstance(option, FamilyOption):
id_list = []
@@ -413,9 +444,11 @@ class CommandLineReport:
if father:
fname = name_displayer.display(father)
# Translators: needed for French, Hebrew and Arabic
text = _("%(id)s:\t%(father)s, %(mother)s"
) % {'id': family.get_gramps_id(),
'father': fname, 'mother': mname}
text = _("%(id)s:\t%(father)s, %(mother)s") % {
"id": family.get_gramps_id(),
"father": fname,
"mother": mname,
}
id_list.append(text)
self.options_help[name].append(id_list)
elif isinstance(option, NoteOption):
@@ -443,13 +476,14 @@ class CommandLineReport:
elif isinstance(option, TextOption):
self.options_help[name].append(
"A list of text values. Each entry in the list "
"represents one line of text.")
"represents one line of text."
)
elif isinstance(option, EnumeratedListOption):
ilist = []
for (value, description) in option.get_items():
tabs = '\t'
for value, description in option.get_items():
tabs = "\t"
try:
tabs = '\t\t' if len(value) < 10 else '\t'
tabs = "\t\t" if len(value) < 10 else "\t"
except TypeError: # Value is a number, use just one tab.
pass
val = "%s%s%s" % (value, tabs, description)
@@ -459,13 +493,19 @@ class CommandLineReport:
self.options_help[name].append(option.get_help())
else:
print(_("Unknown option: %s") % option, file=sys.stderr)
print(_(" Valid options are:") +
_(", ").join(list(self.options_dict.keys())), # Arabic OK
file=sys.stderr)
print(_(" Use '%(donottranslate)s' to see description "
print(
_(" Valid options are:")
+ _(", ").join(list(self.options_dict.keys())), # Arabic OK
file=sys.stderr,
)
print(
_(
" Use '%(donottranslate)s' to see description "
"and acceptable values"
) % {'donottranslate' : "show=option"},
file=sys.stderr)
)
% {"donottranslate": "show=option"},
file=sys.stderr,
)
def parse_options(self):
"""
@@ -477,9 +517,9 @@ class CommandLineReport:
menu = self.option_class.menu
menu_opt_names = menu.get_all_option_names()
_format_str = self.options_str_dict.pop('off', None)
_format_str = self.options_str_dict.pop("off", None)
if _format_str:
self.options_dict['off'] = _format_str
self.options_dict["off"] = _format_str
self.css_filename = None
_chosen_format = None
@@ -488,13 +528,13 @@ class CommandLineReport:
if self.category in [CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK]:
if self.category == CATEGORY_TEXT:
plugins = self.__textdoc_plugins
self.css_filename = self.options_dict['css']
self.css_filename = self.options_dict["css"]
elif self.category == CATEGORY_DRAW:
plugins = self.__drawdoc_plugins
elif self.category == CATEGORY_BOOK:
plugins = self.__bookdoc_plugins
for plugin in plugins:
if plugin.get_extension() == self.options_dict['off']:
if plugin.get_extension() == self.options_dict["off"]:
self.format = plugin.get_basedoc()
self.doc_option_class = plugin.get_doc_option_class()
if self.format is None:
@@ -505,7 +545,7 @@ class CommandLineReport:
_chosen_format = plugin.get_extension()
elif self.category == CATEGORY_GRAPHVIZ:
for graph_format in graphdoc.FORMATS:
if graph_format['type'] == self.options_dict['off']:
if graph_format["type"] == self.options_dict["off"]:
if not self.format: # choose the first one, not the last
self.format = graph_format["class"]
if self.format is None:
@@ -514,7 +554,7 @@ class CommandLineReport:
_chosen_format = graphdoc.FORMATS[0]["type"]
elif self.category == CATEGORY_TREE:
for tree_format in treedoc.FORMATS:
if tree_format['type'] == self.options_dict['off']:
if tree_format["type"] == self.options_dict["off"]:
if not self.format: # choose the first one, not the last
self.format = tree_format["class"]
if self.format is None:
@@ -524,24 +564,33 @@ class CommandLineReport:
else:
self.format = None
if _chosen_format and _format_str:
print(_("Ignoring '%(notranslate1)s=%(notranslate2)s' "
print(
_(
"Ignoring '%(notranslate1)s=%(notranslate2)s' "
"and using '%(notranslate1)s=%(notranslate3)s'."
) % {'notranslate1' : "off",
'notranslate2' : self.options_dict['off'],
'notranslate3' : _chosen_format},
file=sys.stderr)
print(_("Use '%(notranslate)s' to see valid values."
) % {'notranslate' : "show=off"}, file=sys.stderr)
)
% {
"notranslate1": "off",
"notranslate2": self.options_dict["off"],
"notranslate3": _chosen_format,
},
file=sys.stderr,
)
print(
_("Use '%(notranslate)s' to see valid values.")
% {"notranslate": "show=off"},
file=sys.stderr,
)
self.do_doc_options()
for opt in self.options_str_dict:
if opt in self.options_dict:
self.options_dict[opt] = _convert_str_to_match_type(
self.options_str_dict[opt], self.options_dict[opt])
self.options_str_dict[opt], self.options_dict[opt]
)
self.option_class.handler.options_dict[
opt] = self.options_dict[opt]
self.option_class.handler.options_dict[opt] = self.options_dict[opt]
if menu and opt in menu_opt_names:
option = menu.get_option_by_name(opt)
@@ -549,28 +598,34 @@ class CommandLineReport:
else:
print(_("Ignoring unknown option: %s") % opt, file=sys.stderr)
print(_(" Valid options are:"),
print(
_(" Valid options are:"),
_(", ").join(list(self.options_dict.keys())), # Arabic OK
file=sys.stderr)
print(_(" Use '%(donottranslate)s' to see description "
file=sys.stderr,
)
print(
_(
" Use '%(donottranslate)s' to see description "
"and acceptable values"
) % {'donottranslate' : "show=option"},
file=sys.stderr)
)
% {"donottranslate": "show=option"},
file=sys.stderr,
)
self.option_class.handler.output = self.options_dict['of']
self.option_class.handler.output = self.options_dict["of"]
self.paper = paper_sizes[0] # make sure one exists
for paper in paper_sizes:
if paper.get_name() == self.options_dict['papers']:
if paper.get_name() == self.options_dict["papers"]:
self.paper = paper
self.option_class.handler.set_paper(self.paper)
self.orien = self.options_dict['papero']
self.orien = self.options_dict["papero"]
self.marginl = self.options_dict['paperml']
self.marginr = self.options_dict['papermr']
self.margint = self.options_dict['papermt']
self.marginb = self.options_dict['papermb']
self.marginl = self.options_dict["paperml"]
self.marginr = self.options_dict["papermr"]
self.margint = self.options_dict["papermt"]
self.marginb = self.options_dict["papermb"]
if self.category in (CATEGORY_TEXT, CATEGORY_DRAW):
default_style = StyleSheet()
@@ -593,8 +648,7 @@ class CommandLineReport:
return # this docgen type has no options
try:
if issubclass(self.doc_option_class, object):
self.doc_options = self.doc_option_class(self.raw_name,
self.database)
self.doc_options = self.doc_option_class(self.raw_name, self.database)
doc_options_dict = self.doc_options.options_dict
except TypeError:
self.doc_options = self.doc_option_class
@@ -605,7 +659,8 @@ class CommandLineReport:
docgen_opt = docgen_menu.get_option(DOCGEN_OPTIONS, oname)
if oname in self.options_str_dict and oname in doc_options_dict:
doc_options_dict[oname] = _convert_str_to_match_type(
self.options_str_dict[oname], doc_options_dict[oname])
self.options_str_dict[oname], doc_options_dict[oname]
)
self.options_str_dict.pop(oname)
if oname in doc_options_dict:
docgen_opt.set_value(doc_options_dict[oname])
@@ -620,26 +675,29 @@ class CommandLineReport:
"""
if not self.show:
return
elif self.show == 'all':
elif self.show == "all":
print(_(" Available options:"))
for key in sorted(self.options_dict.keys()):
if key in self.options_help:
opt = self.options_help[key]
# Make the output nicer to read, assume a tab has 8 spaces
tabs = '\t\t' if len(key) < 10 else '\t'
tabs = "\t\t" if len(key) < 10 else "\t"
optmsg = " %s%s%s (%s)" % (key, tabs, opt[1], opt[0])
else:
optmsg = " %s%s%s" % (key, tabs,
_('(no help available)'))
optmsg = " %s%s%s" % (key, tabs, _("(no help available)"))
print(optmsg)
print(_(" Use '%(donottranslate)s' to see description "
print(
_(
" Use '%(donottranslate)s' to see description "
"and acceptable values"
) % {'donottranslate' : "show=option"})
)
% {"donottranslate": "show=option"}
)
elif self.show in self.options_help:
opt = self.options_help[self.show]
tabs = '\t\t' if len(self.show) < 10 else '\t'
tabs = "\t\t" if len(self.show) < 10 else "\t"
print(_(" Available values are:"))
print(' %s%s%s (%s)' % (self.show, tabs, opt[1], opt[0]))
print(" %s%s%s (%s)" % (self.show, tabs, opt[1], opt[0]))
vals = opt[2]
if isinstance(vals, (list, tuple)):
for val in vals:
@@ -649,26 +707,28 @@ class CommandLineReport:
else:
# there was a show option given, but the option is invalid
print(_("option '%(optionname)s' not valid. "
print(
_(
"option '%(optionname)s' not valid. "
"Use '%(donottranslate)s' to see all valid options."
) % {'optionname' : self.show,
'donottranslate' : "show=all"},
file=sys.stderr)
)
% {"optionname": self.show, "donottranslate": "show=all"},
file=sys.stderr,
)
# ------------------------------------------------------------------------
#
# Command-line report generic task
#
# ------------------------------------------------------------------------
def cl_report(database, name, category, report_class, options_class,
options_str_dict):
def cl_report(database, name, category, report_class, options_class, options_str_dict):
"""
function to actually run the selected report
"""
err_msg = _("Failed to write report. ")
clr = CommandLineReport(database, name, category, options_class,
options_str_dict)
clr = CommandLineReport(database, name, category, options_class, options_str_dict)
# Exit here if show option was given
if clr.show:
@@ -680,21 +740,43 @@ def cl_report(database, name, category, report_class, options_class,
if clr.doc_options:
clr.option_class.handler.doc = clr.format(
clr.selected_style,
PaperStyle(clr.paper, clr.orien, clr.marginl,
clr.marginr, clr.margint, clr.marginb),
clr.doc_options)
PaperStyle(
clr.paper,
clr.orien,
clr.marginl,
clr.marginr,
clr.margint,
clr.marginb,
),
clr.doc_options,
)
else:
clr.option_class.handler.doc = clr.format(
clr.selected_style,
PaperStyle(clr.paper, clr.orien, clr.marginl,
clr.marginr, clr.margint, clr.marginb))
PaperStyle(
clr.paper,
clr.orien,
clr.marginl,
clr.marginr,
clr.margint,
clr.marginb,
),
)
elif category in [CATEGORY_GRAPHVIZ, CATEGORY_TREE]:
clr.option_class.handler.doc = clr.format(
clr.option_class,
PaperStyle(clr.paper, clr.orien, clr.marginl,
clr.marginr, clr.margint, clr.marginb))
if (clr.css_filename is not None
and hasattr(clr.option_class.handler.doc, 'set_css_filename')):
PaperStyle(
clr.paper,
clr.orien,
clr.marginl,
clr.marginr,
clr.margint,
clr.marginb,
),
)
if clr.css_filename is not None and hasattr(
clr.option_class.handler.doc, "set_css_filename"
):
clr.option_class.handler.doc.set_css_filename(clr.css_filename)
my_report = report_class(database, clr.option_class, User())
my_report.doc.init()
@@ -720,6 +802,7 @@ def cl_report(database, name, category, report_class, options_class,
except:
traceback.print_exc()
def run_report(db, name, **options_str_dict):
"""
Given a database, run a given report.
@@ -757,15 +840,15 @@ def run_report(db, name, **options_str_dict):
report_class = getattr(mod, pdata.reportclass)
options_class = getattr(mod, pdata.optionclass)
if category in (CATEGORY_BOOK, CATEGORY_CODE):
options_class(db, name, category,
options_str_dict)
options_class(db, name, category, options_str_dict)
else:
clr = cl_report(db, name, category,
report_class, options_class,
options_str_dict)
clr = cl_report(
db, name, category, report_class, options_class, options_str_dict
)
return clr
return clr
# ------------------------------------------------------------------------
#
# Function to write books from command line
@@ -777,22 +860,25 @@ def cl_book(database, name, book, options_str_dict):
which in turn runs whatever reports the book has in it
"""
clr = CommandLineReport(database, name, CATEGORY_BOOK,
ReportOptions, options_str_dict)
clr = CommandLineReport(
database, name, CATEGORY_BOOK, ReportOptions, options_str_dict
)
# Exit here if show option was given
if clr.show:
return
# write report
doc = clr.format(None,
PaperStyle(clr.paper, clr.orien, clr.marginl,
clr.marginr, clr.margint, clr.marginb))
doc = clr.format(
None,
PaperStyle(
clr.paper, clr.orien, clr.marginl, clr.marginr, clr.margint, clr.marginb
),
)
user = User()
rptlist = []
selected_style = StyleSheet()
for item in book.get_item_list():
# The option values were loaded magically by the book parser.
# But they still need to be applied to the menu options.
opt_dict = item.option_class.options_dict
@@ -804,9 +890,10 @@ def cl_book(database, name, book, options_str_dict):
item.option_class.set_document(doc)
report_class = item.get_write_item()
obj = (write_book_item(database,
report_class, item.option_class, user),
item.get_translated_name())
obj = (
write_book_item(database, report_class, item.option_class, user),
item.get_translated_name(),
)
if obj:
append_styles(selected_style, item)
rptlist.append(obj)
@@ -817,7 +904,7 @@ def cl_book(database, name, book, options_str_dict):
newpage = 0
err_msg = _("Failed to make '%s' report.")
try:
for (rpt, name) in rptlist:
for rpt, name in rptlist:
if newpage:
doc.page_break()
newpage = 1
@@ -831,6 +918,7 @@ def cl_book(database, name, book, options_str_dict):
if msg2:
print(msg2, file=sys.stderr)
# ------------------------------------------------------------------------
#
# Generic task function for book

View File

@@ -25,6 +25,7 @@ import unittest
from unittest.mock import Mock
from ..argparser import ArgParser
class TestArgParser(unittest.TestCase):
def setUp(self):
pass
@@ -37,44 +38,43 @@ class TestArgParser(unittest.TestCase):
return (str(ap.errors).find("option " + option) >= 0, ap)
def test_wrong_argument_triggers_option_error(self):
bad,ap = self.triggers_option_error('--I-am-a-wrong-argument')
bad, ap = self.triggers_option_error("--I-am-a-wrong-argument")
assert bad, ap.__dict__
def test_y_shortopt_sets_auto_accept(self):
bad, ap = self.triggers_option_error('-y')
bad, ap = self.triggers_option_error("-y")
self.assertFalse(bad)
expected_errors = [(
'Error parsing the arguments',
'Error parsing the arguments: [ -y ] \n' +
'To use in the command-line mode, supply at least one input file to process.'
)]
self.assertEqual(
expected_errors,
ap.errors
expected_errors = [
(
"Error parsing the arguments",
"Error parsing the arguments: [ -y ] \n"
+ "To use in the command-line mode, supply at least one input file to process.",
)
]
self.assertEqual(expected_errors, ap.errors)
self.assertTrue(ap.auto_accept)
def test_yes_longopt_sets_auto_accept(self):
bad,ap = self.triggers_option_error('--yes')
bad, ap = self.triggers_option_error("--yes")
assert not bad, ap.errors
assert ap.auto_accept
def test_q_shortopt_sets_quiet(self):
bad,ap = self.triggers_option_error('-q')
bad, ap = self.triggers_option_error("-q")
assert not bad, ap.errors
assert ap.quiet
def test_quiet_longopt_sets_quiet(self):
bad,ap = self.triggers_option_error('--quiet')
bad, ap = self.triggers_option_error("--quiet")
assert not bad, ap.errors
assert ap.quiet
def test_quiet_exists_by_default(self):
ap = self.create_parser()
assert hasattr(ap,'quiet')
assert hasattr(ap, "quiet")
def test_auto_accept_unset_by_default(self):
ap = self.create_parser()
@@ -83,20 +83,20 @@ class TestArgParser(unittest.TestCase):
def test_exception(self):
argument_parser = self.create_parser("-O")
expected_errors = [(
'Error parsing the arguments',
'option -O requires argument\n'
'Error parsing the arguments: [ -O ] \n'
'Type gramps --help for an overview of commands, or read the manual pages.'
)]
self.assertEqual(
expected_errors,
argument_parser.errors
expected_errors = [
(
"Error parsing the arguments",
"option -O requires argument\n"
"Error parsing the arguments: [ -O ] \n"
"Type gramps --help for an overview of commands, or read the manual pages.",
)
]
self.assertEqual(expected_errors, argument_parser.errors)
def test_option_with_multiple_arguments(self):
argument_parser = self.create_parser('-l', 'family_tree_name')
self.assertEqual(argument_parser.database_names, ['family_tree_name'])
argument_parser = self.create_parser("-l", "family_tree_name")
self.assertEqual(argument_parser.database_names, ["family_tree_name"])
if __name__ == "__main__":
unittest.main()

View File

@@ -44,8 +44,8 @@ ddir = os.path.dirname(__file__)
min1r = os.path.join(ddir, "min1r.ged")
out_ged = os.path.join(ddir, "test_out.ged")
example_copy = os.path.join(ddir, "copy.gramps")
example = os.path.join(ddir, "..", "..", "..",
"example", "gramps", "data.gramps")
example = os.path.join(ddir, "..", "..", "..", "example", "gramps", "data.gramps")
class Test(unittest.TestCase):
def setUp(self):
@@ -70,8 +70,7 @@ class Test(unittest.TestCase):
def test1_setup_works(self):
self.assertTrue(os.path.exists(ddir), "data dir %r exists" % ddir)
self.assertTrue(os.path.exists(min1r), "data file %r exists" % min1r)
self.assertFalse(os.path.exists(out_ged),
"NO out file %r yet" % out_ged)
self.assertFalse(os.path.exists(out_ged), "NO out file %r yet" % out_ged)
# This tests the fix for bug #1331-1334
# read trivial gedcom input, write gedcom output
@@ -79,13 +78,11 @@ class Test(unittest.TestCase):
ifile = min1r
ofile = out_ged
gcmd = [sys.executable, "Gramps.py", "-i", ifile, "-e", ofile]
process = subprocess.Popen(gcmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
process = subprocess.Popen(
gcmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
result_str, err_str = process.communicate()
self.assertEqual(process.returncode, 0,
"executed CLI command %r" % gcmd)
self.assertEqual(process.returncode, 0, "executed CLI command %r" % gcmd)
# simple validation o output
self.assertTrue(os.path.isfile(ofile), "output file created")
with open(ofile) as f:
@@ -98,13 +95,11 @@ class Test(unittest.TestCase):
ifile = min1r
ofile = out_ged
gcmd = [sys.executable, "-m", "gramps", "-i", ifile, "-e", ofile]
process = subprocess.Popen(gcmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
process = subprocess.Popen(
gcmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
result_str, err_str = process.communicate()
self.assertEqual(process.returncode, 0,
"executed CLI command %r" % gcmd)
self.assertEqual(process.returncode, 0, "executed CLI command %r" % gcmd)
# simple validation o output
self.assertTrue(os.path.isfile(ofile), "output file created")
with open(ofile) as f:
@@ -114,39 +109,47 @@ class Test(unittest.TestCase):
class UnicodeTest(unittest.TestCase):
def setUp(self):
from gramps.cli.clidbman import CLIDbManager
from gramps.gen.config import set as setconfig, get as getconfig
from gramps.gen.dbstate import DbState
self.newpath = os.path.join(os.path.dirname(__file__),
'\u0393\u03c1\u03b1\u03bc\u03c3\u03c0')
self.newtitle = 'Gr\u00e4mps T\u00e9st'
self.newpath = os.path.join(
os.path.dirname(__file__), "\u0393\u03c1\u03b1\u03bc\u03c3\u03c0"
)
self.newtitle = "Gr\u00e4mps T\u00e9st"
os.makedirs(self.newpath)
self.old_path = getconfig('database.path')
setconfig('database.path', self.newpath)
self.old_path = getconfig("database.path")
setconfig("database.path", self.newpath)
self.cli = CLIDbManager(DbState())
def tearDown(self):
from gramps.gen.config import set as setconfig
for (dirpath, dirnames, filenames) in os.walk(self.newpath, False):
for dirpath, dirnames, filenames in os.walk(self.newpath, False):
for afile in filenames:
os.remove(os.path.join(dirpath, afile))
for adir in dirnames:
os.rmdir(os.path.join(dirpath, adir))
os.rmdir(self.newpath)
setconfig('database.path', self.old_path)
setconfig("database.path", self.old_path)
# Test that clidbman will open files in a path containing
# arbitrary Unicode characters.
def test4_arbitrary_uncode_path(self):
(dbpath, title) = self.cli.create_new_db_cli(self.newtitle)
self.assertEqual(self.newpath, os.path.dirname(dbpath),
"Compare paths %s and %s" % (repr(self.newpath),
repr(dbpath)))
self.assertEqual(self.newtitle, title, "Compare titles %s and %s" %
(repr(self.newtitle), repr(title)))
self.assertEqual(
self.newpath,
os.path.dirname(dbpath),
"Compare paths %s and %s" % (repr(self.newpath), repr(dbpath)),
)
self.assertEqual(
self.newtitle,
title,
"Compare titles %s and %s" % (repr(self.newtitle), repr(title)),
)
class CLITest(unittest.TestCase):
def tearDown(self):
@@ -167,6 +170,7 @@ class CLITest(unittest.TestCase):
def test1b_cli(self):
self.call("-O", "Test: test1_cli", "--export", example_copy)
if __name__ == "__main__":
unittest.main()

View File

@@ -26,12 +26,14 @@ from unittest.mock import Mock, patch
from .. import user
import sys
class TestUser:
TITLE = "Testing prompt"
MSG = "Choices are hard. Nevertheless, please choose!"
ACCEPT = "To be"
REJECT = "Not to be"
class TestUser_prompt(unittest.TestCase):
def setUp(self):
self.real_user = user.User()
@@ -40,10 +42,10 @@ class TestUser_prompt(unittest.TestCase):
self.user._input = Mock(spec=input)
def test_default_fileout_has_write(self):
assert hasattr(self.real_user._fileout, 'write')
assert hasattr(self.real_user._fileout, "write")
def test_default_input(self):
assert self.real_user._input.__name__.endswith('input')
assert self.real_user._input.__name__.endswith("input")
def test_prompt_returns_True_if_ACCEPT_entered(self):
self.user._input.configure_mock(return_value=TestUser.ACCEPT)
@@ -59,9 +61,14 @@ class TestUser_prompt(unittest.TestCase):
), "False expected!"
self.user._input.assert_called_once_with()
def assert_prompt_contains_text(self, text,
title=TestUser.TITLE, msg=TestUser.MSG,
accept=TestUser.ACCEPT, reject=TestUser.REJECT):
def assert_prompt_contains_text(
self,
text,
title=TestUser.TITLE,
msg=TestUser.MSG,
accept=TestUser.ACCEPT,
reject=TestUser.REJECT,
):
self.user._input.configure_mock(return_value=TestUser.REJECT)
self.user.prompt(title, msg, accept, reject)
for call in self.user._fileout.method_calls:
@@ -69,9 +76,12 @@ class TestUser_prompt(unittest.TestCase):
for a in args:
if a.find(text) >= 0:
return
self.assertTrue(False,
self.assertTrue(
False,
"'{}' never printed in prompt: {}".format(
text, self.user._fileout.method_calls))
text, self.user._fileout.method_calls
),
)
def test_prompt_contains_title_text(self):
self.assert_prompt_contains_text(TestUser.TITLE)
@@ -101,13 +111,14 @@ class TestUser_prompt(unittest.TestCase):
def test_EOFError_in_prompt_caught_as_False(self):
self.user._input.configure_mock(
side_effect = EOFError,
return_value = TestUser.REJECT)
side_effect=EOFError, return_value=TestUser.REJECT
)
assert not self.user.prompt(
TestUser.TITLE, TestUser.MSG, TestUser.ACCEPT, TestUser.REJECT
), "False expected!"
self.user._input.assert_called_once_with()
class TestUser_quiet(unittest.TestCase):
def setUp(self):
self.user = user.User(quiet=True)
@@ -120,11 +131,12 @@ class TestUser_quiet(unittest.TestCase):
self.user.end_progress()
def tearDown(self):
assert len(self.user._fileout.method_calls
) == 0, list(self.user._fileout.method_calls)
assert len(self.user._fileout.method_calls) == 0, list(
self.user._fileout.method_calls
)
class TestUser_progress(unittest.TestCase):
def setUp(self):
self.user = user.User()
self.user._fileout = Mock(spec=sys.stderr)
@@ -136,18 +148,18 @@ class TestUser_progress(unittest.TestCase):
self.user._fileout.reset_mock()
self.assertTrue(
len(self.user._fileout.method_calls) == 0,
list(self.user._fileout.method_calls))
list(self.user._fileout.method_calls),
)
with self.user.progress("Foo", "Bar", 0) as step:
for i in range(10):
step()
# Output using `with' differs from one with `progress_...'
self.assertEqual(self.expected_output,
list(self.user._fileout.method_calls))
self.assertEqual(self.expected_output, list(self.user._fileout.method_calls))
def test_ends_progress_upon_exception_in_with(self):
with patch('gramps.cli.user.User.end_progress') as MockEP:
with patch("gramps.cli.user.User.end_progress") as MockEP:
try:
with self.user.progress("Foo", "Bar", 0) as step:
raise Exception()
@@ -161,5 +173,6 @@ class TestUser_progress(unittest.TestCase):
self.user.step_progress()
self.user.end_progress()
if __name__ == "__main__":
unittest.main()

View File

@@ -28,6 +28,7 @@ The User class provides basic interaction with the user.
#
# ------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from gramps.gen.const import URL_BUGHOME
from gramps.gen import user
@@ -37,7 +38,8 @@ from gramps.gen import user
# Private Constants
#
# ------------------------------------------------------------------------
_SPINNER = ['|', '/', '-', '\\']
_SPINNER = ["|", "/", "-", "\\"]
# -------------------------------------------------------------------------
#
@@ -49,9 +51,16 @@ class User(user.UserBase):
This class provides a means to interact with the user via CLI.
It implements the interface in :class:`.gen.user.UserBase`
"""
def __init__(self, callback=None, error=None,
auto_accept=False, quiet=False,
uistate=None, dbstate=None):
def __init__(
self,
callback=None,
error=None,
auto_accept=False,
quiet=False,
uistate=None,
dbstate=None,
):
"""
Init.
@@ -59,8 +68,8 @@ class User(user.UserBase):
:type error: function(title, error)
"""
user.UserBase.__init__(self, callback, error, uistate, dbstate)
self.steps = 0;
self.current_step = 0;
self.steps = 0
self.current_step = 0
self._input = input
def yes(*args, **kwargs):
@@ -69,8 +78,9 @@ class User(user.UserBase):
if auto_accept:
self.prompt = yes
if quiet:
self.begin_progress = self.end_progress = self.step_progress = \
self._default_callback = yes
self.begin_progress = (
self.end_progress
) = self.step_progress = self._default_callback = yes
def begin_progress(self, title, message, steps):
"""
@@ -88,7 +98,7 @@ class User(user.UserBase):
"""
self._fileout.write(message)
self.steps = steps
self.current_step = 0;
self.current_step = 0
if self.steps == 0:
self._fileout.write(_SPINNER[self.current_step])
else:
@@ -112,8 +122,15 @@ class User(user.UserBase):
"""
self._fileout.write("\r100%\n")
def prompt(self, title, message, accept_label, reject_label,
parent=None, default_label=None):
def prompt(
self,
title,
message,
accept_label,
reject_label,
parent=None,
default_label=None,
):
"""
Prompt the user with a message to select an alternative.
@@ -142,10 +159,8 @@ class User(user.UserBase):
reject_text = "[%s]" % reject_text
default = False
text = "{t}\n{m} ({y}/{n}): ".format(
t = title,
m = message,
y = accept_text,
n = reject_text)
t=title, m=message, y=accept_text, n=reject_text
)
print(text, file=self._fileout) # TODO: python 3.3 add flush=True
try:
reply = self._input()
@@ -198,10 +213,15 @@ class User(user.UserBase):
"""
self.notify_error(
_("Low level database corruption detected"),
_("Gramps has detected a problem in the underlying "
_(
"Gramps has detected a problem in the underlying "
"database. This can sometimes be repaired from "
"the Family Tree Manager. Select the database and "
'click on the Repair button') + '\n\n' + error)
"click on the Repair button"
)
+ "\n\n"
+ error,
)
def notify_db_repair(self, error):
"""
@@ -214,14 +234,18 @@ class User(user.UserBase):
These exact strings are also in gui/dialog.py -- keep them in sync
"""
self.notify_error(
_('Error detected in database'),
_('Gramps has detected an error in the database. This can '
_("Error detected in database"),
_(
"Gramps has detected an error in the database. This can "
'usually be resolved by running the "Check and Repair Database" '
'tool.\n\nIf this problem continues to exist after running this '
'tool, please file a bug report at '
'%(gramps_bugtracker_url)s\n\n'
) % {'gramps_bugtracker_url' : URL_BUGHOME}
+ error + '\n\n')
"tool.\n\nIf this problem continues to exist after running this "
"tool, please file a bug report at "
"%(gramps_bugtracker_url)s\n\n"
)
% {"gramps_bugtracker_url": URL_BUGHOME}
+ error
+ "\n\n",
)
def info(self, msg1, infotext, parent=None, monospaced=False):
"""

View File

@@ -23,5 +23,16 @@ The gen module provides packages that are common to all gramps
interfaces (gui, cli and web).
"""
__all__ = ["datehandler", "db", "display", "filters", "lib", "merge",
"mime", "plug", "proxy", "simple", "utils"]
__all__ = [
"datehandler",
"db",
"display",
"filters",
"lib",
"merge",
"mime",
"plug",
"proxy",
"simple",
"utils",
]

View File

@@ -44,12 +44,15 @@ import logging
from .const import USER_CONFIG, USER_DATA, USER_HOME, VERSION_DIR
from .utils.configmanager import ConfigManager
from .const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# _T_ is a gramps-defined keyword -- see po/update_po.py and po/genpot.sh
def _T_(value, context=''): # enable deferred translations
def _T_(value, context=""): # enable deferred translations
return "%s\x04%s" % (context, value) if context else value
# ---------------------------------------------------------------
#
# Constants
@@ -57,6 +60,7 @@ def _T_(value, context=''): # enable deferred translations
# ---------------------------------------------------------------
INIFILE = os.path.join(VERSION_DIR, "gramps.ini")
# ---------------------------------------------------------------
#
# Module functions
@@ -66,38 +70,47 @@ def register(key, value):
"""Module shortcut to register key, value"""
return CONFIGMAN.register(key, value)
def get(key):
"""Module shortcut to get value from key"""
return CONFIGMAN.get(key)
def get_default(key):
"""Module shortcut to get default from key"""
return CONFIGMAN.get_default(key)
def has_default(key):
"""Module shortcut to get see if there is a default for key"""
return CONFIGMAN.has_default(key)
def get_sections():
"""Module shortcut to get all section names of settings"""
return CONFIGMAN.get_sections()
def get_section_settings(section):
"""Module shortcut to get all settings of a section"""
return CONFIGMAN.get_section_settings(section)
def set(key, value):
"""Module shortcut to set value from key"""
return CONFIGMAN.set(key, value)
def is_set(key):
"""Module shortcut to set value from key"""
return CONFIGMAN.is_set(key)
def save(filename=None):
"""Module shortcut to save config file"""
return CONFIGMAN.save(filename)
def connect(key, func):
"""
Module shortcut to connect a key to a callback func.
@@ -105,22 +118,27 @@ def connect(key, func):
"""
return CONFIGMAN.connect(key, func)
def disconnect(callback_id):
"""Module shortcut to remove callback by ID number"""
return CONFIGMAN.disconnect(callback_id)
def reset(key=None):
"""Module shortcut to reset some or all config data"""
return CONFIGMAN.reset(key)
def load(filename=None, oldstyle=False):
"""Module shortcut to load an INI file into config data"""
return CONFIGMAN.load(filename, oldstyle)
def emit(key):
"""Module shortcut to call all callbacks associated with key"""
return CONFIGMAN.emit(key)
# ---------------------------------------------------------------
#
# Register the system-wide settings in a singleton config manager
@@ -129,224 +147,248 @@ def emit(key):
CONFIGMAN = ConfigManager(INIFILE, "plugins")
register('behavior.addmedia-image-dir', '')
register('behavior.addmedia-relative-path', False)
register('behavior.autoload', False)
register('behavior.avg-generation-gap', 20)
register('behavior.check-for-addon-updates', 0)
register('behavior.check-for-addon-update-types', ["new"])
register('behavior.last-check-for-addon-updates', "1970/01/01")
register('behavior.previously-seen-addon-updates', [])
register('behavior.do-not-show-previously-seen-addon-updates', True)
register('behavior.date-about-range', 50)
register('behavior.date-after-range', 50)
register('behavior.date-before-range', 50)
register('behavior.generation-depth', 15)
register('behavior.max-age-prob-alive', 110)
register('behavior.max-sib-age-diff', 20)
register('behavior.min-generation-years', 13)
register('behavior.owner-warn', False)
register('behavior.immediate-warn', False)
register('behavior.pop-plugin-status', False)
register('behavior.recent-export-type', 3)
register('behavior.runcheck', False)
register('behavior.spellcheck', False)
register('behavior.startup', 0)
register('behavior.surname-guessing', 0)
register('behavior.translator-needed', True)
register('behavior.use-tips', False)
register('behavior.welcome', 100)
register('behavior.web-search-url', 'http://google.com/#&q=%(text)s')
register('behavior.addons-url', 'https://raw.githubusercontent.com/gramps-project/addons/master/gramps52')
register('behavior.addons-projects',
[['Gramps', 'https://raw.githubusercontent.com/gramps-project/addons/master/gramps52', True]])
register('behavior.addons-allow-install', False)
register("behavior.addmedia-image-dir", "")
register("behavior.addmedia-relative-path", False)
register("behavior.autoload", False)
register("behavior.avg-generation-gap", 20)
register("behavior.check-for-addon-updates", 0)
register("behavior.check-for-addon-update-types", ["new"])
register("behavior.last-check-for-addon-updates", "1970/01/01")
register("behavior.previously-seen-addon-updates", [])
register("behavior.do-not-show-previously-seen-addon-updates", True)
register("behavior.date-about-range", 50)
register("behavior.date-after-range", 50)
register("behavior.date-before-range", 50)
register("behavior.generation-depth", 15)
register("behavior.max-age-prob-alive", 110)
register("behavior.max-sib-age-diff", 20)
register("behavior.min-generation-years", 13)
register("behavior.owner-warn", False)
register("behavior.immediate-warn", False)
register("behavior.pop-plugin-status", False)
register("behavior.recent-export-type", 3)
register("behavior.runcheck", False)
register("behavior.spellcheck", False)
register("behavior.startup", 0)
register("behavior.surname-guessing", 0)
register("behavior.translator-needed", True)
register("behavior.use-tips", False)
register("behavior.welcome", 100)
register("behavior.web-search-url", "http://google.com/#&q=%(text)s")
register(
"behavior.addons-url",
"https://raw.githubusercontent.com/gramps-project/addons/master/gramps52",
)
register(
"behavior.addons-projects",
[
[
"Gramps",
"https://raw.githubusercontent.com/gramps-project/addons/master/gramps52",
True,
]
],
)
register("behavior.addons-allow-install", False)
register('csv.dialect', 'excel')
register('csv.delimiter', ',')
register("csv.dialect", "excel")
register("csv.delimiter", ",")
register('database.backend', 'sqlite')
register('database.compress-backup', True)
register('database.backup-path', USER_HOME)
register('database.backup-on-exit', True)
register('database.autobackup', 0)
register('database.path', os.path.join(USER_DATA, 'grampsdb'))
register('database.host', '')
register('database.port', '')
register("database.backend", "sqlite")
register("database.compress-backup", True)
register("database.backup-path", USER_HOME)
register("database.backup-on-exit", True)
register("database.autobackup", 0)
register("database.path", os.path.join(USER_DATA, "grampsdb"))
register("database.host", "")
register("database.port", "")
register('export.proxy-order',
[["privacy", 0],
["living", 0],
["person", 0],
["note", 0],
["reference", 0]]
register(
"export.proxy-order",
[["privacy", 0], ["living", 0], ["person", 0], ["note", 0], ["reference", 0]],
)
register('geography.center-lon', 0.0)
register('geography.lock', False)
register('geography.center-lat', 0.0)
register('geography.map_service', 1)
register('geography.zoom', 0)
register('geography.zoom_when_center', 12)
register('geography.show_cross', False)
register('geography.path', "")
register('geography.use-keypad', True)
register('geography.personal-map', "")
register("geography.center-lon", 0.0)
register("geography.lock", False)
register("geography.center-lat", 0.0)
register("geography.map_service", 1)
register("geography.zoom", 0)
register("geography.zoom_when_center", 12)
register("geography.show_cross", False)
register("geography.path", "")
register("geography.use-keypad", True)
register("geography.personal-map", "")
# note that other calls to "register" are done in realtime (when
# needed), for instance to four 'interface.clipboard' variables --
# so do a recursive grep for "setup_configs" to see all the (base) names
register('interface.dont-ask', False)
register('interface.view-categories',
["Dashboard", "People", "Relationships", "Families",
"Ancestry", "Events", "Places", "Geography", "Sources",
"Citations", "Repositories", "Media", "Notes"])
register('interface.filter', False)
register('interface.fullscreen', False)
register('interface.grampletbar-close', False)
register('interface.ignore-gexiv2', False)
register('interface.ignore-pil', False)
register('interface.ignore-osmgpsmap', False)
register('interface.main-window-height', 500)
register('interface.main-window-horiz-position', 15)
register('interface.main-window-vert-position', 10)
register('interface.main-window-width', 775)
register('interface.mapservice', 'OpenStreetMap')
register('interface.open-with-default-viewer', False)
register('interface.pedview-layout', 0)
register('interface.pedview-show-images', True)
register('interface.pedview-show-marriage', False)
register('interface.pedview-tree-size', 5)
register('interface.pedview-tree-direction', 2)
register('interface.pedview-show-unknown-people', False)
register('interface.place-name-height', 100)
register('interface.place-name-width', 450)
register('interface.sidebar-text', True)
register('interface.size-checked', False)
register('interface.statusbar', 1)
register('interface.toolbar-on', True)
register('interface.toolbar-text', False)
register('interface.hide-lds', False)
register('interface.toolbar-clipboard', True)
register('interface.toolbar-addons', True)
register('interface.toolbar-preference', True)
register('interface.toolbar-reports', True)
register('interface.toolbar-tools', True)
register('interface.view', True)
register('interface.surname-box-height', 150)
register('interface.treemodel-cache-size', 1000)
register("interface.dont-ask", False)
register(
"interface.view-categories",
[
"Dashboard",
"People",
"Relationships",
"Families",
"Ancestry",
"Events",
"Places",
"Geography",
"Sources",
"Citations",
"Repositories",
"Media",
"Notes",
],
)
register("interface.filter", False)
register("interface.fullscreen", False)
register("interface.grampletbar-close", False)
register("interface.ignore-gexiv2", False)
register("interface.ignore-pil", False)
register("interface.ignore-osmgpsmap", False)
register("interface.main-window-height", 500)
register("interface.main-window-horiz-position", 15)
register("interface.main-window-vert-position", 10)
register("interface.main-window-width", 775)
register("interface.mapservice", "OpenStreetMap")
register("interface.open-with-default-viewer", False)
register("interface.pedview-layout", 0)
register("interface.pedview-show-images", True)
register("interface.pedview-show-marriage", False)
register("interface.pedview-tree-size", 5)
register("interface.pedview-tree-direction", 2)
register("interface.pedview-show-unknown-people", False)
register("interface.place-name-height", 100)
register("interface.place-name-width", 450)
register("interface.sidebar-text", True)
register("interface.size-checked", False)
register("interface.statusbar", 1)
register("interface.toolbar-on", True)
register("interface.toolbar-text", False)
register("interface.hide-lds", False)
register("interface.toolbar-clipboard", True)
register("interface.toolbar-addons", True)
register("interface.toolbar-preference", True)
register("interface.toolbar-reports", True)
register("interface.toolbar-tools", True)
register("interface.view", True)
register("interface.surname-box-height", 150)
register("interface.treemodel-cache-size", 1000)
register('paths.recent-export-dir', USER_HOME)
register('paths.recent-file', '')
register('paths.recent-import-dir', USER_HOME)
register('paths.report-directory', USER_HOME)
register('paths.website-directory', USER_HOME)
register('paths.website-cms-uri', '')
register('paths.website-cal-uri', '')
register('paths.website-extra-page-uri', '')
register('paths.website-extra-page-name', '')
register('paths.quick-backup-directory', USER_HOME)
register('paths.quick-backup-filename',
"%(filename)s_%(year)d-%(month)02d-%(day)02d.%(extension)s")
register("paths.recent-export-dir", USER_HOME)
register("paths.recent-file", "")
register("paths.recent-import-dir", USER_HOME)
register("paths.report-directory", USER_HOME)
register("paths.website-directory", USER_HOME)
register("paths.website-cms-uri", "")
register("paths.website-cal-uri", "")
register("paths.website-extra-page-uri", "")
register("paths.website-extra-page-name", "")
register("paths.quick-backup-directory", USER_HOME)
register(
"paths.quick-backup-filename",
"%(filename)s_%(year)d-%(month)02d-%(day)02d.%(extension)s",
)
register('preferences.quick-backup-include-mode', False)
register('preferences.date-format', 0)
register('preferences.calendar-format-report', 0)
register('preferences.calendar-format-input', 0)
register('preferences.february-29', 0) # 0: 02/28; 1: 03/01; 2: only the 02/29
register('preferences.cprefix', 'C%04d')
register('preferences.default-source', False)
register('preferences.tag-on-import', False)
register('preferences.tag-on-import-format', _("Imported %Y/%m/%d %H:%M:%S"))
register('preferences.eprefix', 'E%04d')
register('preferences.family-warn', True)
register('preferences.fprefix', 'F%04d')
register('preferences.hide-ep-msg', False)
register('preferences.invalid-date-format', "<b>%s</b>")
register('preferences.iprefix', 'I%04d')
register('preferences.name-format', 1)
register('preferences.place-format', 0)
register('preferences.place-auto', True)
register('preferences.coord-format', 0)
register('preferences.patronimic-surname', False)
register('preferences.no-given-text', "[%s]" % _("Missing Given Name"))
register('preferences.no-record-text', "[%s]" % _("Missing Record"))
register('preferences.no-surname-text', "[%s]" % _("Missing Surname"))
register('preferences.nprefix', 'N%04d')
register('preferences.online-maps', False)
register('preferences.oprefix', 'O%04d')
register('preferences.paper-metric', 0)
register('preferences.paper-preference', 'Letter')
register('preferences.pprefix', 'P%04d')
register('preferences.private-given-text', "%s" % _T_("[Living]"))
register('preferences.private-record-text', "[%s]" % _("Private Record"))
register('preferences.private-surname-text', "%s" % _T_("[Living]"))
register('preferences.rprefix', 'R%04d')
register('preferences.sprefix', 'S%04d')
register('preferences.use-last-view', False)
register('preferences.last-view', '')
register('preferences.last-views', [])
register('preferences.family-relation-type', 3) # UNKNOWN
register('preferences.age-display-precision', 1)
register('preferences.age-after-death', True)
register('preferences.cite-plugin', 'cite-default')
register("preferences.quick-backup-include-mode", False)
register("preferences.date-format", 0)
register("preferences.calendar-format-report", 0)
register("preferences.calendar-format-input", 0)
register("preferences.february-29", 0) # 0: 02/28; 1: 03/01; 2: only the 02/29
register("preferences.cprefix", "C%04d")
register("preferences.default-source", False)
register("preferences.tag-on-import", False)
register("preferences.tag-on-import-format", _("Imported %Y/%m/%d %H:%M:%S"))
register("preferences.eprefix", "E%04d")
register("preferences.family-warn", True)
register("preferences.fprefix", "F%04d")
register("preferences.hide-ep-msg", False)
register("preferences.invalid-date-format", "<b>%s</b>")
register("preferences.iprefix", "I%04d")
register("preferences.name-format", 1)
register("preferences.place-format", 0)
register("preferences.place-auto", True)
register("preferences.coord-format", 0)
register("preferences.patronimic-surname", False)
register("preferences.no-given-text", "[%s]" % _("Missing Given Name"))
register("preferences.no-record-text", "[%s]" % _("Missing Record"))
register("preferences.no-surname-text", "[%s]" % _("Missing Surname"))
register("preferences.nprefix", "N%04d")
register("preferences.online-maps", False)
register("preferences.oprefix", "O%04d")
register("preferences.paper-metric", 0)
register("preferences.paper-preference", "Letter")
register("preferences.pprefix", "P%04d")
register("preferences.private-given-text", "%s" % _T_("[Living]"))
register("preferences.private-record-text", "[%s]" % _("Private Record"))
register("preferences.private-surname-text", "%s" % _T_("[Living]"))
register("preferences.rprefix", "R%04d")
register("preferences.sprefix", "S%04d")
register("preferences.use-last-view", False)
register("preferences.last-view", "")
register("preferences.last-views", [])
register("preferences.family-relation-type", 3) # UNKNOWN
register("preferences.age-display-precision", 1)
register("preferences.age-after-death", True)
register("preferences.cite-plugin", "cite-default")
register('colors.scheme', 0)
register('colors.male-alive', ['#b8cee6', '#1f344a'])
register('colors.male-dead', ['#b8cee6', '#2d3039'])
register('colors.female-alive', ['#feccf0', '#62242D'])
register('colors.female-dead', ['#feccf0', '#3a292b'])
register('colors.other-alive', ['#94ef9e', '#285b27'])
register('colors.other-dead', ['#94ef9e', '#062304'])
register('colors.unknown-alive', ['#f3dbb6', '#75507B'])
register('colors.unknown-dead', ['#f3dbb6', '#35103b'])
register('colors.family', ['#eeeeee', '#454545'])
register('colors.family-married', ['#eeeeee', '#454545'])
register('colors.family-unmarried', ['#eeeeee', '#454545'])
register('colors.family-civil-union', ['#eeeeee', '#454545'])
register('colors.family-unknown', ['#eeeeee', '#454545'])
register('colors.family-divorced', ['#ffdede', '#5c3636'])
register('colors.home-person', ['#bbe68a', '#304918'])
register('colors.border-male-alive', ['#1f4986', '#171d26'])
register('colors.border-male-dead', ['#000000', '#000000'])
register('colors.border-female-alive', ['#861f69', '#261111'])
register('colors.border-female-dead', ['#000000', '#000000'])
register('colors.border-other-alive', ['#2a5f16', '#26a269'])
register('colors.border-other-dead', ['#000000', '#000000'])
register('colors.border-unknown-alive', ['#8e5801', '#8e5801'])
register('colors.border-unknown-dead', ['#000000', '#000000'])
register('colors.border-family', ['#cccccc', '#252525'])
register('colors.border-family-divorced', ['#ff7373', '#720b0b'])
register("colors.scheme", 0)
register("colors.male-alive", ["#b8cee6", "#1f344a"])
register("colors.male-dead", ["#b8cee6", "#2d3039"])
register("colors.female-alive", ["#feccf0", "#62242D"])
register("colors.female-dead", ["#feccf0", "#3a292b"])
register("colors.other-alive", ["#94ef9e", "#285b27"])
register("colors.other-dead", ["#94ef9e", "#062304"])
register("colors.unknown-alive", ["#f3dbb6", "#75507B"])
register("colors.unknown-dead", ["#f3dbb6", "#35103b"])
register("colors.family", ["#eeeeee", "#454545"])
register("colors.family-married", ["#eeeeee", "#454545"])
register("colors.family-unmarried", ["#eeeeee", "#454545"])
register("colors.family-civil-union", ["#eeeeee", "#454545"])
register("colors.family-unknown", ["#eeeeee", "#454545"])
register("colors.family-divorced", ["#ffdede", "#5c3636"])
register("colors.home-person", ["#bbe68a", "#304918"])
register("colors.border-male-alive", ["#1f4986", "#171d26"])
register("colors.border-male-dead", ["#000000", "#000000"])
register("colors.border-female-alive", ["#861f69", "#261111"])
register("colors.border-female-dead", ["#000000", "#000000"])
register("colors.border-other-alive", ["#2a5f16", "#26a269"])
register("colors.border-other-dead", ["#000000", "#000000"])
register("colors.border-unknown-alive", ["#8e5801", "#8e5801"])
register("colors.border-unknown-dead", ["#000000", "#000000"])
register("colors.border-family", ["#cccccc", "#252525"])
register("colors.border-family-divorced", ["#ff7373", "#720b0b"])
register('researcher.researcher-addr', '')
register('researcher.researcher-locality', '')
register('researcher.researcher-city', '')
register('researcher.researcher-country', '')
register('researcher.researcher-email', '')
register('researcher.researcher-name', '')
register('researcher.researcher-phone', '')
register('researcher.researcher-postal', '')
register('researcher.researcher-state', '')
register("researcher.researcher-addr", "")
register("researcher.researcher-locality", "")
register("researcher.researcher-city", "")
register("researcher.researcher-country", "")
register("researcher.researcher-email", "")
register("researcher.researcher-name", "")
register("researcher.researcher-phone", "")
register("researcher.researcher-postal", "")
register("researcher.researcher-state", "")
register('plugin.hiddenplugins', [])
register('plugin.addonplugins', [])
register("plugin.hiddenplugins", [])
register("plugin.addonplugins", [])
register('utf8.in-use', False)
register('utf8.selected-font', '')
register('utf8.death-symbol', 2)
register('utf8.birth-symbol', "*")
register('utf8.baptism-symbol', "~")
register('utf8.marriage-symbol', "oo")
register('utf8.engaged-symbol', "o")
register('utf8.divorce-symbol', "o|o")
register('utf8.partner-symbol', "o-o")
register('utf8.dead-symbol', "")
register('utf8.buried-symbol', "[]")
register('utf8.cremated-symbol', "")
register('utf8.killed-symbol', "x")
register("utf8.in-use", False)
register("utf8.selected-font", "")
register("utf8.death-symbol", 2)
register("utf8.birth-symbol", "*")
register("utf8.baptism-symbol", "~")
register("utf8.marriage-symbol", "oo")
register("utf8.engaged-symbol", "o")
register("utf8.divorce-symbol", "o|o")
register("utf8.partner-symbol", "o-o")
register("utf8.dead-symbol", "")
register("utf8.buried-symbol", "[]")
register("utf8.cremated-symbol", "")
register("utf8.killed-symbol", "x")
if __debug__: # enable a simple CLI test to see if the datestrings exist
register('test.january', _("January", "localized lexeme inflections"))
register("test.january", _("January", "localized lexeme inflections"))
# ---------------------------------------------------------------
#
@@ -361,14 +403,13 @@ if not os.path.exists(CONFIGMAN.filename):
if os.path.exists(os.path.join(USER_CONFIG, "keys.ini")):
# read it in old style:
logging.warning("Importing old key file 'keys.ini'...")
CONFIGMAN.load(os.path.join(USER_CONFIG, "keys.ini"),
oldstyle=True)
CONFIGMAN.load(os.path.join(USER_CONFIG, "keys.ini"), oldstyle=True)
logging.warning("Done importing old key file 'keys.ini'")
# other version upgrades here...
# check previous version of gramps:
fullpath, filename = os.path.split(CONFIGMAN.filename)
fullpath, previous = os.path.split(fullpath)
match = re.match(r'gramps(\d*)', previous)
match = re.match(r"gramps(\d*)", previous)
if match:
# cycle back looking for previous versions of gramps
for i in range(1, 20): # check back 2 gramps versions
@@ -380,14 +421,13 @@ if not os.path.exists(CONFIGMAN.filename):
# Perhaps addings specific list of versions to check
# -----------------------------------------
digits = str(int(match.groups()[0]) - i)
previous_grampsini = os.path.join(fullpath, "gramps" + digits,
filename)
previous_grampsini = os.path.join(fullpath, "gramps" + digits, filename)
if os.path.exists(previous_grampsini):
logging.warning("Importing old config file '%s'...",
previous_grampsini)
logging.warning("Importing old config file '%s'...", previous_grampsini)
CONFIGMAN.load(previous_grampsini)
logging.warning("Done importing old config file '%s'",
previous_grampsini)
logging.warning(
"Done importing old config file '%s'", previous_grampsini
)
break
# ---------------------------------------------------------------
@@ -398,5 +438,5 @@ if not os.path.exists(CONFIGMAN.filename):
CONFIGMAN.load()
config = CONFIGMAN
if config.get('database.backend') == 'bsddb':
config.set('database.backend', 'sqlite')
if config.get("database.backend") == "bsddb":
config.set("database.backend", "sqlite")

View File

@@ -65,10 +65,10 @@ URL_BUGHOME = "http://gramps-project.org/bugs"
URL_BUGTRACKER = "http://gramps-project.org/bugs/bug_report_page.php"
URL_WIKISTRING = "http://gramps-project.org/wiki/index.php?title="
URL_MANUAL_PAGE = "Gramps_%s_Wiki_Manual" % major_version
URL_MANUAL_DATA = '%s_-_Entering_and_editing_data:_detailed' % URL_MANUAL_PAGE
URL_MANUAL_SECT1 = '%s_-_part_1' % URL_MANUAL_DATA
URL_MANUAL_SECT2 = '%s_-_part_2' % URL_MANUAL_DATA
URL_MANUAL_SECT3 = '%s_-_part_3' % URL_MANUAL_DATA
URL_MANUAL_DATA = "%s_-_Entering_and_editing_data:_detailed" % URL_MANUAL_PAGE
URL_MANUAL_SECT1 = "%s_-_part_1" % URL_MANUAL_DATA
URL_MANUAL_SECT2 = "%s_-_part_2" % URL_MANUAL_DATA
URL_MANUAL_SECT3 = "%s_-_part_3" % URL_MANUAL_DATA
WIKI_FAQ = "FAQ"
WIKI_KEYBINDINGS = "Gramps_%s_Wiki_Manual_-_Keybindings" % major_version
WIKI_EXTRAPLUGINS = "%s_Addons" % major_version
@@ -79,7 +79,7 @@ WIKI_EXTRAPLUGINS_RAWDATA = "Plugins%s&action=raw" % major_version
# Mime Types
#
# -------------------------------------------------------------------------
APP_FAMTREE = 'x-directory/normal'
APP_FAMTREE = "x-directory/normal"
APP_GRAMPS = "application/x-gramps"
APP_GRAMPS_XML = "application/x-gramps-xml"
APP_GEDCOM = "application/x-gedcom"
@@ -94,36 +94,35 @@ APP_VCARD = ["text/x-vcard", "text/x-vcalendar"]
# Windows apparently uses USERPROFILE
#
# -------------------------------------------------------------------------
if 'GRAMPSHOME' in os.environ:
USER_HOME = get_env_var('GRAMPSHOME')
HOME_DIR = os.path.join(USER_HOME, 'gramps')
elif 'USERPROFILE' in os.environ:
USER_HOME = get_env_var('USERPROFILE')
if 'APPDATA' in os.environ:
HOME_DIR = os.path.join(get_env_var('APPDATA'), 'gramps')
if "GRAMPSHOME" in os.environ:
USER_HOME = get_env_var("GRAMPSHOME")
HOME_DIR = os.path.join(USER_HOME, "gramps")
elif "USERPROFILE" in os.environ:
USER_HOME = get_env_var("USERPROFILE")
if "APPDATA" in os.environ:
HOME_DIR = os.path.join(get_env_var("APPDATA"), "gramps")
else:
HOME_DIR = os.path.join(USER_HOME, 'gramps')
HOME_DIR = os.path.join(USER_HOME, "gramps")
else:
USER_HOME = get_env_var('HOME')
HOME_DIR = os.path.join(USER_HOME, '.gramps')
USER_HOME = get_env_var("HOME")
HOME_DIR = os.path.join(USER_HOME, ".gramps")
ORIG_HOME_DIR = HOME_DIR
if 'SAFEMODE' in os.environ:
if 'USERPROFILE' in os.environ:
USER_HOME = get_env_var('USERPROFILE')
if "SAFEMODE" in os.environ:
if "USERPROFILE" in os.environ:
USER_HOME = get_env_var("USERPROFILE")
else:
USER_HOME = get_env_var('HOME')
HOME_DIR = get_env_var('SAFEMODE')
USER_HOME = get_env_var("HOME")
HOME_DIR = get_env_var("SAFEMODE")
if (os.path.exists(HOME_DIR) or 'GRAMPSHOME' in os.environ
or 'SAFEMODE' in os.environ):
if os.path.exists(HOME_DIR) or "GRAMPSHOME" in os.environ or "SAFEMODE" in os.environ:
USER_DATA = HOME_DIR
USER_CONFIG = HOME_DIR
USER_CACHE = HOME_DIR
else:
USER_DATA = os.path.join(GLib.get_user_data_dir(), 'gramps')
USER_CONFIG = os.path.join(GLib.get_user_config_dir(), 'gramps')
USER_CACHE = os.path.join(GLib.get_user_cache_dir(), 'gramps')
USER_DATA = os.path.join(GLib.get_user_data_dir(), "gramps")
USER_CONFIG = os.path.join(GLib.get_user_config_dir(), "gramps")
USER_CACHE = os.path.join(GLib.get_user_cache_dir(), "gramps")
USER_PICTURES = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES)
if not USER_PICTURES:
@@ -144,9 +143,19 @@ THUMB_LARGE = os.path.join(THUMB_DIR, "large")
USER_PLUGINS = os.path.join(USER_DATA_VERSION, "plugins")
USER_CSS = os.path.join(USER_DATA, "css")
# dirs checked/made for each Gramps session
USER_DIRLIST = (USER_HOME, USER_CACHE, USER_CONFIG, USER_DATA, VERSION_DIR,
USER_DATA_VERSION, THUMB_DIR, THUMB_NORMAL, THUMB_LARGE,
USER_PLUGINS, USER_CSS)
USER_DIRLIST = (
USER_HOME,
USER_CACHE,
USER_CONFIG,
USER_DATA,
VERSION_DIR,
USER_DATA_VERSION,
THUMB_DIR,
THUMB_NORMAL,
THUMB_LARGE,
USER_PLUGINS,
USER_CSS,
)
# -------------------------------------------------------------------------
@@ -158,8 +167,8 @@ USER_DIRLIST = (USER_HOME, USER_CACHE, USER_CONFIG, USER_DATA, VERSION_DIR,
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
sys.path.insert(0, ROOT_DIR)
git_revision = get_git_revision(ROOT_DIR).replace('\n', '')
if sys.platform == 'win32' and git_revision == "":
git_revision = get_git_revision(ROOT_DIR).replace("\n", "")
if sys.platform == "win32" and git_revision == "":
git_revision = get_git_revision(os.path.split(ROOT_DIR)[1])
if DEV_VERSION:
VERSION += git_revision
@@ -196,7 +205,7 @@ ICON = os.path.join(IMAGE_DIR, "gramps.png")
LOGO = os.path.join(IMAGE_DIR, "logo.png")
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")
# -------------------------------------------------------------------------
#
@@ -230,18 +239,19 @@ ENV = {
# -------------------------------------------------------------------------
GRAMPS_LOCALE = GrampsLocale(localedir=_resources.locale_dir)
_ = GRAMPS_LOCALE.translation.sgettext
GTK_GETTEXT_DOMAIN = 'gtk30'
GTK_GETTEXT_DOMAIN = "gtk30"
# -------------------------------------------------------------------------
#
# About box information
#
# -------------------------------------------------------------------------
COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" \
"© 2007-2022 The Gramps Developers"
COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" "© 2007-2022 The Gramps Developers"
COMMENTS = _(
"Gramps\n (Genealogical Research and Analysis "
"Management Programming System)\n"
"is a personal genealogy program.")
"is a personal genealogy program."
)
AUTHORS = [
"Alexander Roitman",
"Benny Malengier",
@@ -252,13 +262,13 @@ AUTHORS = [
"Martin Hawlisch",
"Richard Taylor",
"Tim Waugh",
"John Ralls"
"John Ralls",
]
AUTHORS_FILE = os.path.join(DATA_DIR, "authors.xml")
DOCUMENTERS = [
'Alexander Roitman',
"Alexander Roitman",
]
# -------------------------------------------------------------------------
@@ -275,8 +285,8 @@ NO_SURNAME = "(%s)" % _("none", "surname")
NO_GIVEN = "(%s)" % _("none", "given-name")
ARABIC_COMMA = "،"
ARABIC_SEMICOLON = "؛"
DOCGEN_OPTIONS = 'Docgen Options'
COLON = _(':') # Translators: needed for French, ignore otherwise
DOCGEN_OPTIONS = "Docgen Options"
COLON = _(":") # Translators: needed for French, ignore otherwise
# -------------------------------------------------------------------------
#
@@ -307,8 +317,7 @@ LONGOPTS = [
"help",
"import=",
"load-modules=",
"list"
"name=",
"list" "name=",
"oaf-activate-iid=",
"oaf-ior-fd=",
"oaf-private",
@@ -333,7 +342,7 @@ LONGOPTS = [
SHORTOPTS = "O:U:P:C:i:e:f:a:p:d:c:r:lLthuv?syqSD:"
GRAMPS_UUID = uuid.UUID('516cd010-5a41-470f-99f8-eb22f1098ad6')
GRAMPS_UUID = uuid.UUID("516cd010-5a41-470f-99f8-eb22f1098ad6")
# -------------------------------------------------------------------------
#
@@ -357,7 +366,8 @@ BACKGROUND_GRAD_AGE = 5
BACKGROUND_SINGLE_COLOR = 6
BACKGROUND_GRAD_PERIOD = 7
GENCOLOR = {
BACKGROUND_SCHEME1: ((255, 63, 0),
BACKGROUND_SCHEME1: (
(255, 63, 0),
(255, 175, 15),
(255, 223, 87),
(255, 255, 111),
@@ -367,14 +377,19 @@ GENCOLOR = {
(231, 23, 255),
(231, 23, 121),
(210, 170, 124),
(189, 153, 112)),
BACKGROUND_SCHEME2: ((229, 191, 252),
(189, 153, 112),
),
BACKGROUND_SCHEME2: (
(229, 191, 252),
(191, 191, 252),
(191, 222, 252),
(183, 219, 197),
(206, 246, 209)),
BACKGROUND_WHITE: ((255, 255, 255),
(255, 255, 255),),
(206, 246, 209),
),
BACKGROUND_WHITE: (
(255, 255, 255),
(255, 255, 255),
),
}
MAX_AGE = 100

View File

@@ -50,6 +50,7 @@ WINDOWS = ["Windows", "win32"]
#
# -------------------------------------------------------------------------
def lin():
"""
Return True if a linux system
@@ -59,6 +60,7 @@ def lin():
return True
return False
def mac():
"""
Return True if a Macintosh system
@@ -67,6 +69,7 @@ def mac():
return True
return False
def win():
"""
Return True if a windows system
@@ -75,10 +78,12 @@ def win():
return True
return False
## The following functions do import gtk, but only when called. They
## should only be called after translation system has been
## initialized!
def is_quartz():
"""
Tests to see if Python is currently running with gtk and
@@ -87,8 +92,9 @@ def is_quartz():
if mac():
try:
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
except ImportError:
@@ -96,6 +102,7 @@ def is_quartz():
return Gdk.Display.get_default().__class__.__name__.endswith("QuartzDisplay")
return False
def has_display():
"""
Tests to see if Python is currently running with gtk
@@ -105,27 +112,29 @@ def has_display():
temp, sys.argv = sys.argv, sys.argv[:1]
try:
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version("Gtk", "3.0")
gi.require_version("Gdk", "3.0")
from gi.repository import Gtk
from gi.repository import Gdk
except ImportError:
return False
try:
test = Gtk.init_check(temp) and \
Gdk.Display.get_default()
test = Gtk.init_check(temp) and Gdk.Display.get_default()
sys.argv = temp
return bool(test)
except:
sys.argv = temp
return False
# A couple of places add menu accelerators using <alt>, which doesn't
# work with Gtk-quartz. <Meta> is the usually correct replacement, but
# in one case the key is a number, and <meta>number is used by Spaces
# (a mac feature), so we'll use control instead.
def mod_key():
"""
Returns a string to pass to an accelerator map.
@@ -136,6 +145,7 @@ def mod_key():
return "<alt>"
# Python2 on Windows munges environemnt variables to match the system
# code page. This breaks all manner of things and the workaround
# though a bit ugly, is encapsulated here. Use this to retrieve
@@ -146,21 +156,22 @@ def mod_key():
def get_env_var(name, default=None):
'''
"""
Python2 on Windows can't directly read unicode values from
environment variables. This routine does so using the native C
wide-character function.
'''
"""
if not name or name not in os.environ:
return default
return os.environ[name]
def get_curr_dir():
'''
"""
In Python2 on Windows, os.getcwd() returns a string encoded with
the current code page, which may not be able to correctly handle
an arbitrary unicode character in a path. This function uses the
native GetCurrentDirectory function to return a unicode cwd.
'''
"""
return os.getcwd()

View File

@@ -37,10 +37,17 @@ import logging
# -------------------------------------------------------------------------
from ..utils.grampslocale import GrampsLocale
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
# import prerequisites for localized handlers
from ._datehandler import (LANG, LANG_SHORT, LANG_TO_PARSER, LANG_TO_DISPLAY,
locale_tformat, main_locale)
from ._datehandler import (
LANG,
LANG_SHORT,
LANG_TO_PARSER,
LANG_TO_DISPLAY,
locale_tformat,
main_locale,
)
from . import _datestrings
# Import all the localized handlers
@@ -84,14 +91,14 @@ try:
else:
parser = LANG_TO_PARSER[LANG_SHORT](plocale=dlocale)
except:
logging.warning(
_("Date parser for '%s' not available, using default") % LANG)
logging.warning(_("Date parser for '%s' not available, using default") % LANG)
parser = LANG_TO_PARSER["C"](plocale=dlocale)
# Initialize global displayer
try:
from ..config import config
val = config.get('preferences.date-format')
val = config.get("preferences.date-format")
except:
val = 0
@@ -101,8 +108,7 @@ try:
else:
displayer = LANG_TO_DISPLAY[LANG_SHORT](val, blocale=dlocale)
except:
logging.warning(
_("Date displayer for '%s' not available, using default") % LANG)
logging.warning(_("Date displayer for '%s' not available, using default") % LANG)
displayer = LANG_TO_DISPLAY["C"](val, blocale=dlocale)
@@ -112,17 +118,23 @@ from ._dateutils import *
# set GRAMPS_RESOURCES then: python3 -m gramps.gen.datehandler.__init__
if __name__ == "__main__":
from ._datedisplay import DateDisplay
m = 0
date_handlers = sorted(LANG_TO_DISPLAY.items())
for l, d in date_handlers:
if len(l) != 2 and l not in ('zh_TW'): # Chinese has two date_handlers
if len(l) != 2 and l not in ("zh_TW"): # Chinese has two date_handlers
continue
if l.upper() == l and (l.lower(), d) in date_handlers:
continue # don't need to see the upper-case variant also
m = max(m, len(d.formats))
print("{}: {} {} own-f:{} own-dc:{} own-dg:{}".format(
l, len(d.formats), d.formats,
print(
"{}: {} {} own-f:{} own-dc:{} own-dg:{}".format(
l,
len(d.formats),
d.formats,
d.formats != DateDisplay.formats,
d._display_calendar != DateDisplay._display_calendar,
d._display_gregorian != DateDisplay._display_gregorian))
d._display_gregorian != DateDisplay._display_gregorian,
)
)
print("MAX: ", m)

View File

@@ -41,6 +41,7 @@ from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
from ..const import ARABIC_COMMA
# -------------------------------------------------------------------------
#
# Arabic parser class

View File

@@ -40,13 +40,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Bulgarian parser
#
# -------------------------------------------------------------------------
class DateParserBG(DateParser):
modifier_to_int = {
"преди": Date.MOD_BEFORE,
"пр.": Date.MOD_BEFORE,

View File

@@ -42,13 +42,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Catalan parser
#
# -------------------------------------------------------------------------
class DateParserCA(DateParser):
modifier_to_int = {
"abans de": Date.MOD_BEFORE,
"abans": Date.MOD_BEFORE,

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Czech parser

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Danish parser class

View File

@@ -40,13 +40,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# German parser
#
# -------------------------------------------------------------------------
class DateParserDE(DateParser):
month_to_int = DateParser.month_to_int
# Always add german and austrian name variants no matter what the current
# locale is

View File

@@ -41,6 +41,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Greek parser class

View File

@@ -40,13 +40,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Spanish parser
#
# -------------------------------------------------------------------------
class DateParserES(DateParser):
modifier_to_int = {
"antes de": Date.MOD_BEFORE,
"antes": Date.MOD_BEFORE,
@@ -123,14 +123,30 @@ class DateDisplayES(DateDisplay):
Spanish language date display class.
"""
short_months = ( "", "ene", "feb", "mar", "abr", "may",
"jun", "jul", "ago", "sep", "oct", "nov",
"dic" )
short_months = (
"",
"ene",
"feb",
"mar",
"abr",
"may",
"jun",
"jul",
"ago",
"sep",
"oct",
"nov",
"dic",
)
calendar = (
"", "Juliano", "Hebreo",
"Revolucionario francés", "Persa", "Islámico",
"Sueco"
"",
"Juliano",
"Hebreo",
"Revolucionario francés",
"Persa",
"Islámico",
"Sueco",
)
_mod_str = ("", "antes de ", "después de ", "hacia ", "", "", "")

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Finnish parser
@@ -49,7 +50,6 @@ from ._datehandler import register_datehandler
# - Parsing Finnish is much more complicated than English
# -------------------------------------------------------------------------
class DateParserFI(DateParser):
# NOTE: these need to be in lower case because the "key" comparison
# is done as lower case. In the display method correct capitalization
# can be used.

View File

@@ -44,6 +44,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# French parser
@@ -281,12 +282,10 @@ class DateDisplayFR(DateDisplay):
# this must agree with its locale-specific "formats" definition
year = self._slash_year(date_val[2], date_val[3])
if self.format == 0:
# ISO
return self.display_iso(date_val)
elif self.format == 1:
# numerical
if date_val[2] < 0 or date_val[3]:
@@ -304,7 +303,6 @@ class DateDisplayFR(DateDisplay):
value = value.replace("%Y", str(date_val[2]))
elif self.format == 2:
# day month_name year
if date_val[0] == 0:
@@ -313,10 +311,8 @@ class DateDisplayFR(DateDisplay):
else:
value = "%s %s" % (self.long_months[date_val[1]], year)
else:
value = "%d %s %s" % (date_val[0], self.long_months[date_val[1]], year)
elif self.format == 3:
# day month_abbreviation year
if date_val[0] == 0:
@@ -325,10 +321,8 @@ class DateDisplayFR(DateDisplay):
else:
value = "%s %s" % (self.short_months[date_val[1]], year)
else:
value = "%d %s %s" % (date_val[0], self.short_months[date_val[1]], year)
elif self.format == 4:
# day. month_name year
if date_val[0] == 0:
@@ -337,14 +331,12 @@ class DateDisplayFR(DateDisplay):
else:
value = "%s %s" % (self.long_months[date_val[1]], year)
else:
# base_display :
# value = "%d %s %s" % (date_val[0],
# self.long_months[date_val[1]], year)
value = "%d. %s %s" % (date_val[0], self.long_months[date_val[1]], year)
elif self.format == 5:
# day. month_abbreviation year
if date_val[0] == 0:
@@ -353,7 +345,6 @@ class DateDisplayFR(DateDisplay):
else:
value = "%s %s" % (self.short_months[date_val[1]], year)
else:
# base_display :
# value = "%d %s %s" % (date_val[0],
# self.short_months[date_val[1]], year)
@@ -364,7 +355,6 @@ class DateDisplayFR(DateDisplay):
year,
)
elif self.format == 6:
# month_name day, year
if date_val[0] == 0:
@@ -375,7 +365,6 @@ class DateDisplayFR(DateDisplay):
else:
value = "%s %d, %s" % (self.long_months[date_val[1]], date_val[0], year)
elif self.format == 7:
# month_abbreviation day, year
if date_val[0] == 0:
@@ -390,7 +379,6 @@ class DateDisplayFR(DateDisplay):
year,
)
elif self.format == 8:
# French numerical with 0
if date_val[2] < 0 or date_val[3]:
@@ -399,9 +387,9 @@ class DateDisplayFR(DateDisplay):
if date_val[0] == date_val[1] == 0:
value = str(date_val[2])
else:
value = self.dhformat.replace('%m', str(date_val[1]).zfill(2))
value = value.replace('%d', str(date_val[0]).zfill(2))
value = value.replace('%Y', str(date_val[2]))
value = self.dhformat.replace("%m", str(date_val[1]).zfill(2))
value = value.replace("%d", str(date_val[0]).zfill(2))
value = value.replace("%Y", str(date_val[2]))
else:
return self.display_iso(date_val)

View File

@@ -45,6 +45,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Croatian parser

View File

@@ -41,6 +41,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Hungarian parser
@@ -48,7 +49,6 @@ from ._datehandler import register_datehandler
#
# -------------------------------------------------------------------------
class DateParserHU(DateParser):
month_to_int = DateParser.month_to_int
month_to_int["-"] = 0 # to make the Zero month to work

View File

@@ -42,6 +42,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Icelandic parser class

View File

@@ -42,13 +42,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Italian parser
#
# -------------------------------------------------------------------------
class DateParserIT(DateParser):
modifier_to_int = {
"prima del": Date.MOD_BEFORE,
"prima": Date.MOD_BEFORE,

View File

@@ -43,6 +43,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Japanese parser

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Lithuanian parser

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Norwegian parser class

View File

@@ -46,13 +46,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Dutch parser
#
# -------------------------------------------------------------------------
class DateParserNL(DateParser):
month_to_int = DateParser.month_to_int
# Always add dutch and flemish name variants
# no matter what the current locale is

View File

@@ -43,13 +43,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Polish parser
#
# -------------------------------------------------------------------------
class DateParserPL(DateParser):
month_to_int = DateParser.month_to_int
month_to_int["styczeń"] = 1
month_to_int["sty"] = 1

View File

@@ -42,13 +42,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Portuguese parser
#
# -------------------------------------------------------------------------
class DateParserPT(DateParser):
modifier_to_int = {
"antes de": Date.MOD_BEFORE,
"antes": Date.MOD_BEFORE,

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Russian parser

View File

@@ -40,13 +40,13 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Slovak parser
#
# -------------------------------------------------------------------------
class DateParserSK(DateParser):
modifier_to_int = {
"pred": Date.MOD_BEFORE,
"do": Date.MOD_BEFORE,

View File

@@ -42,6 +42,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Slovenian parser

View File

@@ -43,6 +43,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Serbian parser

View File

@@ -40,6 +40,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Swedish parser class

View File

@@ -42,6 +42,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Ukrainian parser

View File

@@ -42,6 +42,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Simplified-Chinese parser

View File

@@ -42,6 +42,7 @@ from ._dateparser import DateParser
from ._datedisplay import DateDisplay
from ._datehandler import register_datehandler
# -------------------------------------------------------------------------
#
# Traditional-Chinese parser

View File

@@ -52,6 +52,7 @@ from ..const import GRAMPS_LOCALE as glocale
from ..utils.grampslocale import GrampsLocale
from ._datestrings import DateStrings
# _T_ is a gramps-defined keyword -- see po/update_po.py and po/genpot.sh
def _T_(value, context=""): # enable deferred translations
return "%s\x04%s" % (context, value) if context else value

View File

@@ -36,6 +36,7 @@ import os
#
# -------------------------------------------------------------------------
import logging
log = logging.getLogger(".gen.datehandler")
# -------------------------------------------------------------------------
@@ -66,7 +67,7 @@ if not LANG:
LANG = os.environ["LANG"]
if LANG:
LANG_SHORT = LANG.split('_')[0]
LANG_SHORT = LANG.split("_")[0]
else:
LANG_SHORT = "C"
@@ -74,12 +75,12 @@ LANG = str(LANG)
LANG_SHORT = str(LANG_SHORT)
LANG_TO_PARSER = {
'C' : DateParser,
"C": DateParser,
}
LANG_TO_DISPLAY = {
'C' : DateDisplayEn,
'ko_KR' : DateDisplay,
"C": DateDisplayEn,
"ko_KR": DateDisplay,
}
main_locale = {} # this will be augmented by calls to register_datehandler
@@ -87,25 +88,27 @@ main_locale = { } # this will be augmented by calls to register_datehandler
locale_tformat = {} # locale "tformat" (date format) strings
for no_handler in (
('C', ('%d/%m/%Y',)),
('eo_EO', 'eo', 'Esperanto', ('%d/%m/%Y',)), # 'eo_EO' is a placeholder
('he_IL', 'he', 'Hebrew', ('%d/%m/%Y',)),
('sq_AL', 'sq', 'Albanian', ('%Y/%b/%d',)),
('ta_IN', 'ta', 'Tamil', ('%A %d %B %Y',)),
('tr_TR', 'tr', 'Turkish', ('%d/%m/%Y',)),
('vi_VN', 'vi', 'Vietnamese', ('%d/%m/%Y',)),
("C", ("%d/%m/%Y",)),
("eo_EO", "eo", "Esperanto", ("%d/%m/%Y",)), # 'eo_EO' is a placeholder
("he_IL", "he", "Hebrew", ("%d/%m/%Y",)),
("sq_AL", "sq", "Albanian", ("%Y/%b/%d",)),
("ta_IN", "ta", "Tamil", ("%A %d %B %Y",)),
("tr_TR", "tr", "Turkish", ("%d/%m/%Y",)),
("vi_VN", "vi", "Vietnamese", ("%d/%m/%Y",)),
):
format_string = ''
format_string = ""
for possible_format in no_handler:
if isinstance(possible_format, tuple):
format_string = possible_format[0] # pre-seeded date format string
# maintain legacy gramps transformations
format_string = format_string.replace('%y','%Y').replace('-', '/')
format_string = format_string.replace("%y", "%Y").replace("-", "/")
for lang_str in no_handler:
if isinstance(lang_str, tuple): continue
if isinstance(lang_str, tuple):
continue
main_locale[lang_str] = no_handler[0]
locale_tformat[lang_str] = format_string # locale's date format string
def register_datehandler(locales, parse_class, display_class):
"""
Registers the passed date parser class and date displayer
@@ -123,14 +126,15 @@ def register_datehandler(locales,parse_class,display_class):
:param display_class: Class to be associated with displaying
:type display_class: :class:`.DateDisplay`
"""
format_string = ''
format_string = ""
for possible_format in locales: # allow possibly embedding a date format
if isinstance(possible_format, tuple):
format_string = possible_format[0] # pre-seeded date format string
# maintain legacy gramps transformations
format_string = format_string.replace('%y','%Y').replace('-', '/')
format_string = format_string.replace("%y", "%Y").replace("-", "/")
for lang_str in locales:
if isinstance(lang_str, tuple): continue
if isinstance(lang_str, tuple):
continue
LANG_TO_PARSER[lang_str] = parse_class
LANG_TO_DISPLAY[lang_str] = display_class
main_locale[lang_str] = locales[0]
@@ -138,10 +142,11 @@ def register_datehandler(locales,parse_class,display_class):
parse_class._locale = display_class._locale = GrampsLocale(lang=locales[0])
register_datehandler(
('en_GB', 'English_United Kingdom', ("%d/%m/%y",)),
DateParser, DateDisplayGB)
register_datehandler(
('en_US', 'en', 'English_United States', ("%m/%d/%y",)),
DateParser, DateDisplayEn)
("en_GB", "English_United Kingdom", ("%d/%m/%y",)), DateParser, DateDisplayGB
)
register_datehandler(
("en_US", "en", "English_United States", ("%m/%d/%y",)), DateParser, DateDisplayEn
)

View File

@@ -544,30 +544,40 @@ class DateParser:
r"%s\.?(\s+\d+)?\s*,?\s+((\d+)(/\d+)?)?\s*$" % self._mon_str, re.IGNORECASE
)
# this next RE has the (possibly-slashed) year at the string's end
self._text2 = re.compile(r'(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
% self._mon_str, re.IGNORECASE)
self._jtext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
% self._jmon_str, re.IGNORECASE)
self._jtext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
% self._jmon_str, re.IGNORECASE)
self._ftext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
% self._fmon_str, re.IGNORECASE)
self._ftext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
% self._fmon_str, re.IGNORECASE)
self._ptext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
% self._pmon_str, re.IGNORECASE)
self._ptext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
% self._pmon_str, re.IGNORECASE)
self._itext = re.compile(r'%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
% self._imon_str, re.IGNORECASE)
self._itext2 = re.compile(r'(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$'
% self._imon_str, re.IGNORECASE)
self._stext = re.compile(r'%s\.?\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$'
% self._smon_str, re.IGNORECASE)
self._stext2 = re.compile(r'(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$'
% self._smon_str, re.IGNORECASE)
self._numeric = re.compile(
r"((\d+)[/\.]\s*)?((\d+)[/\.]\s*)?(\d+)\s*$")
self._text2 = re.compile(
r"(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$" % self._mon_str, re.IGNORECASE
)
self._jtext = re.compile(
r"%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$" % self._jmon_str, re.IGNORECASE
)
self._jtext2 = re.compile(
r"(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$" % self._jmon_str, re.IGNORECASE
)
self._ftext = re.compile(
r"%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$" % self._fmon_str, re.IGNORECASE
)
self._ftext2 = re.compile(
r"(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$" % self._fmon_str, re.IGNORECASE
)
self._ptext = re.compile(
r"%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$" % self._pmon_str, re.IGNORECASE
)
self._ptext2 = re.compile(
r"(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$" % self._pmon_str, re.IGNORECASE
)
self._itext = re.compile(
r"%s\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$" % self._imon_str, re.IGNORECASE
)
self._itext2 = re.compile(
r"(\d+)?\s+?%s\s*((\d+)(/\d+)?)?\s*$" % self._imon_str, re.IGNORECASE
)
self._stext = re.compile(
r"%s\.?\s+(\d+)?\s*,?\s*((\d+)(/\d+)?)?\s*$" % self._smon_str, re.IGNORECASE
)
self._stext2 = re.compile(
r"(\d+)?\s+?%s\.?\s*((\d+)(/\d+)?)?\s*$" % self._smon_str, re.IGNORECASE
)
self._numeric = re.compile(r"((\d+)[/\.]\s*)?((\d+)[/\.]\s*)?(\d+)\s*$")
self._iso = re.compile(r"(\d+)(/(\d+))?-(\d+)(-(\d+))?\s*$")
self._isotimestamp = re.compile(
r"^\s*?(\d{4})([01]\d)([0123]\d)(?:(?:[012]\d[0-5]\d[0-5]\d)|"

View File

@@ -47,6 +47,7 @@ import logging
log = logging.getLogger(".DateStrings")
# -------------------------------------------------------------------------
#
# DateStrings
@@ -97,7 +98,8 @@ class DateStrings:
_("September", "localized lexeme inflections"),
_("October", "localized lexeme inflections"),
_("November", "localized lexeme inflections"),
_("December", "localized lexeme inflections") )
_("December", "localized lexeme inflections"),
)
self.short_months = (
"",
@@ -116,7 +118,8 @@ class DateStrings:
_("Sep", "localized lexeme inflections - short month form"),
_("Oct", "localized lexeme inflections - short month form"),
_("Nov", "localized lexeme inflections - short month form"),
_("Dec", "localized lexeme inflections - short month form") )
_("Dec", "localized lexeme inflections - short month form"),
)
_ = locale.translation.sgettext
self.alt_long_months = (
@@ -136,7 +139,8 @@ class DateStrings:
_("", "alternative month names for September"),
_("", "alternative month names for October"),
_("", "alternative month names for November"),
_("", "alternative month names for December") )
_("", "alternative month names for December"),
)
self.calendar = (
# Must appear in the order indexed by Date.CAL_... numeric constants

View File

@@ -38,6 +38,7 @@ from ..const import GRAMPS_LOCALE as glocale
from ..lib.date import Date
from . import LANG_TO_DISPLAY, LANG, parser, displayer
# --------------------------------------------------------------
#
# Convenience functions
@@ -54,11 +55,12 @@ def get_date_formats(flocale=glocale):
# trans_text is a defined keyword (see po/update_po.py, po/genpot.sh)
trans_text = flocale.translation.sgettext
try:
return tuple(trans_text(fmt)
for fmt in LANG_TO_DISPLAY[flocale.lang](0).formats)
return tuple(
trans_text(fmt) for fmt in LANG_TO_DISPLAY[flocale.lang](0).formats
)
except:
return tuple(trans_text(fmt)
for fmt in LANG_TO_DISPLAY['C'](0).formats)
return tuple(trans_text(fmt) for fmt in LANG_TO_DISPLAY["C"](0).formats)
def set_format(value):
try:
@@ -66,6 +68,7 @@ def set_format(value):
except:
pass
def set_date(date_base, text):
"""
Set the date of the :class:`.DateBase` instance.
@@ -79,6 +82,7 @@ def set_date(date_base, text):
"""
parser.set_date(date_base.get_date_object(), text)
def get_date(date_base):
"""
Return a string representation of the date of the :class:`.DateBase`
@@ -92,10 +96,12 @@ def get_date(date_base):
"""
return displayer.display(date_base.get_date_object())
def get_date_valid(date_base):
date_obj = date_base.get_date_object()
return date_obj.get_valid()
def format_time(secs):
"""
Format a time in seconds as a date in the preferred date format and a
@@ -103,4 +109,4 @@ def format_time(secs):
"""
t = time.localtime(secs)
d = Date(t.tm_year, t.tm_mon, t.tm_mday)
return displayer.display(d) + time.strftime(' %X', t)
return displayer.display(d) + time.strftime(" %X", t)

View File

@@ -47,7 +47,6 @@ set, we have to convert to unicode.
codeset = glocale.encoding # TODO I don't think "codeset" is used anymore
try:
# here only for the upgrade tool, see _datestrings.py __main__
_deprecated_long_months = (
"",
@@ -114,34 +113,34 @@ except:
_deprecated_long_months = (
"",
time.strftime('%B',(1,1,1,1,1,1,1,1,1)),
time.strftime('%B',(1,2,1,1,1,1,1,1,1)),
time.strftime('%B',(1,3,1,1,1,1,1,1,1)),
time.strftime('%B',(1,4,1,1,1,1,1,1,1)),
time.strftime('%B',(1,5,1,1,1,1,1,1,1)),
time.strftime('%B',(1,6,1,1,1,1,1,1,1)),
time.strftime('%B',(1,7,1,1,1,1,1,1,1)),
time.strftime('%B',(1,8,1,1,1,1,1,1,1)),
time.strftime('%B',(1,9,1,1,1,1,1,1,1)),
time.strftime('%B',(1,10,1,1,1,1,1,1,1)),
time.strftime('%B',(1,11,1,1,1,1,1,1,1)),
time.strftime('%B',(1,12,1,1,1,1,1,1,1)),
time.strftime("%B", (1, 1, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 2, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 3, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 4, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 5, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 6, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 7, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 8, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 9, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 10, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 11, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%B", (1, 12, 1, 1, 1, 1, 1, 1, 1)),
)
_deprecated_short_months = (
"",
time.strftime('%b',(1,1,1,1,1,1,1,1,1)),
time.strftime('%b',(1,2,1,1,1,1,1,1,1)),
time.strftime('%b',(1,3,1,1,1,1,1,1,1)),
time.strftime('%b',(1,4,1,1,1,1,1,1,1)),
time.strftime('%b',(1,5,1,1,1,1,1,1,1)),
time.strftime('%b',(1,6,1,1,1,1,1,1,1)),
time.strftime('%b',(1,7,1,1,1,1,1,1,1)),
time.strftime('%b',(1,8,1,1,1,1,1,1,1)),
time.strftime('%b',(1,9,1,1,1,1,1,1,1)),
time.strftime('%b',(1,10,1,1,1,1,1,1,1)),
time.strftime('%b',(1,11,1,1,1,1,1,1,1)),
time.strftime('%b',(1,12,1,1,1,1,1,1,1)),
time.strftime("%b", (1, 1, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 2, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 3, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 4, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 5, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 6, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 7, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 8, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 9, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 10, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 11, 1, 1, 1, 1, 1, 1, 1)),
time.strftime("%b", (1, 12, 1, 1, 1, 1, 1, 1, 1)),
)
# Gramps day number: Sunday => 1, Monday => 2, etc
@@ -155,22 +154,22 @@ except:
# just a dummy.
_deprecated_long_days = (
"",
time.strftime('%A',(1,1,1,1,1,1,6,1,1)), # Sunday
time.strftime('%A',(1,1,1,1,1,1,0,1,1)), # Monday
time.strftime('%A',(1,1,1,1,1,1,1,1,1)), # Tuesday
time.strftime('%A',(1,1,1,1,1,1,2,1,1)), # Wednesday
time.strftime('%A',(1,1,1,1,1,1,3,1,1)), # Thursday
time.strftime('%A',(1,1,1,1,1,1,4,1,1)), # Friday
time.strftime('%A',(1,1,1,1,1,1,5,1,1)), # Saturday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 6, 1, 1)), # Sunday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 0, 1, 1)), # Monday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 1, 1, 1)), # Tuesday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 2, 1, 1)), # Wednesday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 3, 1, 1)), # Thursday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 4, 1, 1)), # Friday
time.strftime("%A", (1, 1, 1, 1, 1, 1, 5, 1, 1)), # Saturday
)
_deprecated_short_days = (
"",
time.strftime('%a',(1,1,1,1,1,1,6,1,1)), # Sun
time.strftime('%a',(1,1,1,1,1,1,0,1,1)), # Mon
time.strftime('%a',(1,1,1,1,1,1,1,1,1)), # Tue
time.strftime('%a',(1,1,1,1,1,1,2,1,1)), # Wed
time.strftime('%a',(1,1,1,1,1,1,3,1,1)), # Thu
time.strftime('%a',(1,1,1,1,1,1,4,1,1)), # Fri
time.strftime('%a',(1,1,1,1,1,1,5,1,1)), # Sat
time.strftime("%a", (1, 1, 1, 1, 1, 1, 6, 1, 1)), # Sun
time.strftime("%a", (1, 1, 1, 1, 1, 1, 0, 1, 1)), # Mon
time.strftime("%a", (1, 1, 1, 1, 1, 1, 1, 1, 1)), # Tue
time.strftime("%a", (1, 1, 1, 1, 1, 1, 2, 1, 1)), # Wed
time.strftime("%a", (1, 1, 1, 1, 1, 1, 3, 1, 1)), # Thu
time.strftime("%a", (1, 1, 1, 1, 1, 1, 4, 1, 1)), # Fri
time.strftime("%a", (1, 1, 1, 1, 1, 1, 5, 1, 1)), # Sat
)

View File

@@ -28,11 +28,13 @@ import unittest
from ...utils.grampslocale import GrampsLocale
from ...lib.date import Date
class DateDisplayTest(unittest.TestCase):
def setUp(self):
from .._datedisplay import DateDisplayEn
self.display = DateDisplayEn()
self.display_RU = GrampsLocale(lang='ru').date_displayer
self.display_RU = GrampsLocale(lang="ru").date_displayer
def assert_map_key_val(self, m, k, v):
try:
@@ -40,12 +42,14 @@ class DateDisplayTest(unittest.TestCase):
except KeyError:
self.assertTrue(False, list(m.items()))
class DateDisplayCalendarTest(DateDisplayTest):
def test_calendar_gregorian_is_empty(self):
self.assert_map_key_val(self.display.calendar, Date.CAL_GREGORIAN, "")
def test_calendar_julian_RU(self):
self.assert_map_key_val(self.display_RU.calendar, Date.CAL_JULIAN, 'Юлианский')
self.assert_map_key_val(self.display_RU.calendar, Date.CAL_JULIAN, "Юлианский")
# This class tests common functionality in DateDisplay as applied to RU,
# and so it is coupled to translated strings and inflection names
@@ -63,32 +67,31 @@ class DateDisplayInflectionsTestRU(DateDisplayTest):
if month is None:
month = date.get_month()
month_lexeme = self.months[month]
self.assertIn(month_lexeme.forms[inflection],
self.dd.display(date))
self.assertIn(month_lexeme.forms[inflection], self.dd.display(date))
def test_month_only_date_nominative_quality_none(self):
d1945may = Date(1945, 5, 0)
d1945may.set_quality(Date.QUAL_NONE)
self.assertInflectionInDate('И', d1945may)
self.assertInflectionInDate("И", d1945may)
def test_month_only_date_nominative_quality_estimated(self):
d1945may = Date(1945, 5, 0)
d1945may.set_quality(Date.QUAL_ESTIMATED)
self.assertInflectionInDate('Т', d1945may)
self.assertInflectionInDate("Т", d1945may)
def test_month_only_date_nominative_quality_calculated(self):
d1945may = Date(1945, 5, 0)
d1945may.set_quality(Date.QUAL_CALCULATED)
self.assertInflectionInDate('И', d1945may)
self.assertInflectionInDate("И", d1945may)
def test_day_month_date_genitive(self):
d1945may9 = Date(1945, 5, 9)
self.assertInflectionInDate('Р', d1945may9)
self.assertInflectionInDate("Р", d1945may9)
def test_day_month_date_genitiive_quality_estimated(self):
d1945may9 = Date(1945, 5, 9)
d1945may9.set_quality(Date.QUAL_ESTIMATED)
self.assertInflectionInDate('Р', d1945may9)
self.assertInflectionInDate("Р", d1945may9)
def test_before_month_only_date_genitive(self):
d1945may = Date(1945, 5, 0)
@@ -123,8 +126,8 @@ class DateDisplayInflectionsTestRU(DateDisplayTest):
def test_between_month_only_dates_ablative(self):
b1945may_1946may = Date()
b1945may_1946may.set(
modifier=Date.MOD_RANGE,
value=(0, 5, 1945, False, 0, 5, 1946, False))
modifier=Date.MOD_RANGE, value=(0, 5, 1945, False, 0, 5, 1946, False)
)
# TODO hardwired magic numbers! Bad API smell.
for inflecting_format in (3, 4):
self.dd.set_format(inflecting_format)
@@ -136,8 +139,8 @@ class DateDisplayInflectionsTestRU(DateDisplayTest):
def test_month_only_date_span_from_genitive_to_accusative(self):
f1945may_t1946may = Date()
f1945may_t1946may.set(
modifier=Date.MOD_SPAN,
value=(0, 5, 1945, False, 0, 5, 1946, False))
modifier=Date.MOD_SPAN, value=(0, 5, 1945, False, 0, 5, 1946, False)
)
# TODO hardwired magic numbers! Bad API smell.
for inflecting_format in (3, 4):
self.dd.set_format(inflecting_format)
@@ -146,5 +149,6 @@ class DateDisplayInflectionsTestRU(DateDisplayTest):
self.assertIn("с мая", self.dd.display(f1945may_t1946may))
self.assertIn("по май", self.dd.display(f1945may_t1946may))
if __name__ == "__main__":
unittest.main()

View File

@@ -55,6 +55,7 @@ from ...lib import Date, DateError
from ...utils.grampslocale import GrampsLocale, _LOCALE_NAMES
from .. import LANG_TO_PARSER
# -------------------------------------------------------------------------
#
#
@@ -65,7 +66,6 @@ class DateHandlerTest(unittest.TestCase):
config.set("preferences.date-format", 0)
def __base_test_all_languages(self, dates):
languages = [
lang for lang in LANG_TO_PARSER.keys() if lang in _LOCALE_NAMES.keys()
]
@@ -74,7 +74,6 @@ class DateHandlerTest(unittest.TestCase):
self.__test_language(language, dates)
def __test_language(self, language, dates):
locale = GrampsLocale(lang=language)
displayer = locale.date_displayer
parser = locale.date_parser
@@ -90,7 +89,6 @@ class DateHandlerTest(unittest.TestCase):
)
def test_simple(self):
dates = []
for calendar in (Date.CAL_GREGORIAN, Date.CAL_JULIAN):
for newyear in (Date.NEWYEAR_JAN1, Date.NEWYEAR_MAR25, (5, 5)):
@@ -123,7 +121,6 @@ class DateHandlerTest(unittest.TestCase):
self.__base_test_all_languages(dates)
def test_span(self):
dates = []
calendar = Date.CAL_GREGORIAN
for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED):
@@ -207,7 +204,6 @@ class DateHandlerTest(unittest.TestCase):
self.__base_test_all_languages(dates)
def test_textual(self):
dates = []
calendar = Date.CAL_GREGORIAN
modifier = Date.MOD_TEXTONLY

View File

@@ -28,11 +28,13 @@ import unittest
from ...utils.grampslocale import GrampsLocale
from ...lib.date import Date
class DateParserTest(unittest.TestCase):
def setUp(self):
from .._dateparser import DateParser
self.parser = DateParser()
self.parser_RU = GrampsLocale(lang='ru').date_parser
self.parser_RU = GrampsLocale(lang="ru").date_parser
def assert_map_key_val(self, m, k, v):
try:
@@ -41,87 +43,96 @@ class DateParserTest(unittest.TestCase):
self.assertTrue(False, list(m.items()))
def test_month_to_int_jan_is_1(self):
self.assert_map_key_val(self.parser.month_to_int, 'jan', 1)
self.assert_map_key_val(self.parser.month_to_int, "jan", 1)
def test_prefix_table_for_RU_built(self):
self.assertIn('ru_RU', self.parser._langs)
self.assertIn("ru_RU", self.parser._langs)
def test_month_to_int_septem_RU_is_9(self):
self.assert_map_key_val(self.parser.month_to_int, 'сентяб', 9)
self.assert_map_key_val(self.parser.month_to_int, "сентяб", 9)
def test_hebrew_to_int_av_is_12(self):
self.assert_map_key_val(self.parser.hebrew_to_int, 'av', 12)
self.assert_map_key_val(self.parser.hebrew_to_int, 'ав', 12) # RU
self.assert_map_key_val(self.parser.hebrew_to_int, "av", 12)
self.assert_map_key_val(self.parser.hebrew_to_int, "ав", 12) # RU
def test_french_to_int_thermidor_is_11(self):
self.assert_map_key_val(self.parser.french_to_int, 'thermidor', 11)
self.assert_map_key_val(self.parser.french_to_int, 'термидор', 11) # RU
self.assert_map_key_val(self.parser.french_to_int, "thermidor", 11)
self.assert_map_key_val(self.parser.french_to_int, "термидор", 11) # RU
def test_islamic_to_int_ramadan_is_9(self):
self.assert_map_key_val(self.parser.islamic_to_int, 'ramadan', 9)
self.assert_map_key_val(self.parser.islamic_to_int, 'рамадан', 9) # RU
self.assert_map_key_val(self.parser.islamic_to_int, "ramadan", 9)
self.assert_map_key_val(self.parser.islamic_to_int, "рамадан", 9) # RU
def test_persian_to_int_tir_is_4(self):
self.assert_map_key_val(self.parser.persian_to_int, 'tir', 4)
self.assert_map_key_val(self.parser.persian_to_int, 'тир', 4) # RU
self.assert_map_key_val(self.parser.persian_to_int, "tir", 4)
self.assert_map_key_val(self.parser.persian_to_int, "тир", 4) # RU
def test_calendar_to_int_gregorian(self):
self.assert_map_key_val(self.parser.calendar_to_int, 'gregorian', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'g', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'григорианский', Date.CAL_GREGORIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'г', Date.CAL_GREGORIAN)
self.assert_map_key_val(
self.parser.calendar_to_int, "gregorian", Date.CAL_GREGORIAN
)
self.assert_map_key_val(self.parser.calendar_to_int, "g", Date.CAL_GREGORIAN)
self.assert_map_key_val(
self.parser.calendar_to_int, "григорианский", Date.CAL_GREGORIAN
)
self.assert_map_key_val(self.parser.calendar_to_int, "г", Date.CAL_GREGORIAN)
def test_calendar_to_int_julian(self):
self.assert_map_key_val(self.parser.calendar_to_int, 'julian', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'j', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'юлианский', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, 'ю', Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, "julian", Date.CAL_JULIAN)
self.assert_map_key_val(self.parser.calendar_to_int, "j", Date.CAL_JULIAN)
self.assert_map_key_val(
self.parser.calendar_to_int, "юлианский", Date.CAL_JULIAN
)
self.assert_map_key_val(self.parser.calendar_to_int, "ю", Date.CAL_JULIAN)
def test_quarter_1(self):
date = self.parser.parse('q1 1900')
self.assertTrue(date.is_equal(self.parser.parse('Q1 1900')))
date = self.parser.parse("q1 1900")
self.assertTrue(date.is_equal(self.parser.parse("Q1 1900")))
self.assertEqual(date.get_ymd(), (1900, 1, 1))
self.assertEqual(date.get_stop_ymd(), (1900, 3, 31))
self.assertEqual(date.get_modifier(), Date.MOD_RANGE)
def test_quarter_2(self):
date = self.parser.parse('q2 1900')
self.assertTrue(date.is_equal(self.parser.parse('Q2 1900')))
date = self.parser.parse("q2 1900")
self.assertTrue(date.is_equal(self.parser.parse("Q2 1900")))
self.assertEqual(date.get_ymd(), (1900, 4, 1))
self.assertEqual(date.get_stop_ymd(), (1900, 6, 30))
self.assertEqual(date.get_modifier(), Date.MOD_RANGE)
def test_quarter_3(self):
date = self.parser.parse('q3 1900')
self.assertTrue(date.is_equal(self.parser.parse('Q3 1900')))
date = self.parser.parse("q3 1900")
self.assertTrue(date.is_equal(self.parser.parse("Q3 1900")))
self.assertEqual(date.get_ymd(), (1900, 7, 1))
self.assertEqual(date.get_stop_ymd(), (1900, 9, 30))
self.assertEqual(date.get_modifier(), Date.MOD_RANGE)
def test_quarter_4(self):
date = self.parser.parse('q4 1900')
self.assertTrue(date.is_equal(self.parser.parse('Q4 1900')))
date = self.parser.parse("q4 1900")
self.assertTrue(date.is_equal(self.parser.parse("Q4 1900")))
self.assertEqual(date.get_ymd(), (1900, 10, 1))
self.assertEqual(date.get_stop_ymd(), (1900, 12, 31))
self.assertEqual(date.get_modifier(), Date.MOD_RANGE)
def test_quarter_quality_calendar(self):
date = self.parser.parse('calc q1 1900 (julian)')
date = self.parser.parse("calc q1 1900 (julian)")
self.assertEqual(date.get_quality(), Date.QUAL_CALCULATED)
self.assertEqual(date.get_calendar(), Date.CAL_JULIAN)
class Test_generate_variants(unittest.TestCase):
def setUp(self):
from .. import _datestrings
from .._dateparser import _generate_variants
self.ds = ds = _datestrings.DateStrings(GrampsLocale(languages=('ru')))
self.month_variants = list(_generate_variants(
zip(ds.long_months, ds.short_months,
ds.swedish_SV, ds.alt_long_months)))
self.ds = ds = _datestrings.DateStrings(GrampsLocale(languages=("ru")))
self.month_variants = list(
_generate_variants(
zip(ds.long_months, ds.short_months, ds.swedish_SV, ds.alt_long_months)
)
)
def testVariantsSameLengthAsLongMonths(self):
self.assertEqual(len(self.ds.long_months),
len(self.month_variants))
self.assertEqual(len(self.ds.long_months), len(self.month_variants))
def testRussianHasDifferentVariantsForEachMonth(self):
for i in range(1, 13):
@@ -154,5 +165,6 @@ class Test_generate_variants(unittest.TestCase):
v = self.month_variants[5]
self.assertIn("Maj", v)
if __name__ == "__main__":
unittest.main()

View File

@@ -24,16 +24,18 @@ import unittest
from .. import _datestrings
from ...lib.date import Date
class DateStringsTest(unittest.TestCase):
def setUp(self):
from ...utils.grampslocale import GrampsLocale
self.ds = _datestrings.DateStrings(GrampsLocale()) # whatever the default...
self.ds_EN = _datestrings.DateStrings(GrampsLocale(languages='en'))
self.ds_RU = _datestrings.DateStrings(GrampsLocale(languages='ru'))
self.ds_EN = _datestrings.DateStrings(GrampsLocale(languages="en"))
self.ds_RU = _datestrings.DateStrings(GrampsLocale(languages="ru"))
def testTwelfthMonthIsDecember(self):
self.assertEqual(self.ds_EN.long_months[12], 'December')
self.assertEqual(self.ds_EN.short_months[12], 'Dec')
self.assertEqual(self.ds_EN.long_months[12], "December")
self.assertEqual(self.ds_EN.short_months[12], "Dec")
# May is 3-letter in Russian, and so abbreviated form
# will be different for inflections!
@@ -76,5 +78,6 @@ class DateStringsTest(unittest.TestCase):
def testDayNamesLenIs8(self):
self.assertEqual(len(self.ds.long_days), 8)
if __name__ == "__main__":
unittest.main()

View File

@@ -42,6 +42,7 @@ import logging
# -------------------------------------------------------------------------
from ..db.dbconst import DBLOGNAME
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from ..lib.childreftype import ChildRefType
from ..lib.childref import ChildRef
@@ -1264,8 +1265,14 @@ class DbReadBase:
"""
raise NotImplementedError
def load(self, name, callback, mode=None, force_schema_upgrade=False,
force_bsddb_upgrade=False):
def load(
self,
name,
callback,
mode=None,
force_schema_upgrade=False,
force_bsddb_upgrade=False,
):
"""
Open the specified database.
"""
@@ -1295,8 +1302,9 @@ class DbReadBase:
"""
raise NotImplementedError
def set_prefixes(self, person, media, family, source, citation,
place, event, repository, note):
def set_prefixes(
self, person, media, family, source, citation, place, event, repository, note
):
"""
Set the prefixes for the gramps ids for all gramps objects
"""
@@ -1797,10 +1805,9 @@ class DbWriteBase(DbReadBase):
"""
raise NotImplementedError
def add_child_to_family(self, family, child,
mrel=ChildRefType(),
frel=ChildRefType(),
trans=None):
def add_child_to_family(
self, family, child, mrel=ChildRefType(), frel=ChildRefType(), trans=None
):
"""
Adds a child to a family.
"""
@@ -1813,26 +1820,23 @@ class DbWriteBase(DbReadBase):
child.add_parent_family_handle(family.handle)
if trans is None:
with DbTxn(_('Add child to family'), self) as trans:
with DbTxn(_("Add child to family"), self) as trans:
self.commit_family(family, trans)
self.commit_person(child, trans)
else:
self.commit_family(family, trans)
self.commit_person(child, trans)
def remove_child_from_family(self, person_handle, family_handle,
trans=None):
def remove_child_from_family(self, person_handle, family_handle, trans=None):
"""
Remove a person as a child of the family, deleting the family if
it becomes empty.
"""
if trans is None:
with DbTxn(_("Remove child from family"), self) as trans:
self.__remove_child_from_family(person_handle, family_handle,
trans)
self.__remove_child_from_family(person_handle, family_handle, trans)
else:
self.__remove_child_from_family(person_handle, family_handle,
trans)
self.__remove_child_from_family(person_handle, family_handle, trans)
trans.set_description(_("Remove child from family"))
def __remove_child_from_family(self, person_handle, family_handle, trans):
@@ -1845,8 +1849,11 @@ class DbWriteBase(DbReadBase):
person.remove_parent_family_handle(family_handle)
family.remove_child_handle(person_handle)
if (not family.get_father_handle() and not family.get_mother_handle()
and not family.get_child_ref_list()):
if (
not family.get_father_handle()
and not family.get_mother_handle()
and not family.get_child_ref_list()
):
self.remove_family_relationships(family_handle, trans)
else:
self.commit_family(family, trans)
@@ -1874,9 +1881,11 @@ class DbWriteBase(DbReadBase):
else:
family.set_mother_handle(None)
if not family.get_father_handle() and \
not family.get_mother_handle() and \
not family.get_child_ref_list():
if (
not family.get_father_handle()
and not family.get_mother_handle()
and not family.get_child_ref_list()
):
self.remove_family_relationships(family_handle, trans)
else:
self.commit_family(family, trans)
@@ -1885,9 +1894,11 @@ class DbWriteBase(DbReadBase):
if family_handle:
family = self.get_family_from_handle(family_handle)
family.remove_child_handle(person.get_handle())
if not family.get_father_handle() and \
not family.get_mother_handle() and \
not family.get_child_ref_list():
if (
not family.get_father_handle()
and not family.get_mother_handle()
and not family.get_child_ref_list()
):
self.remove_family_relationships(family_handle, trans)
else:
self.commit_family(family, trans)
@@ -1896,7 +1907,7 @@ class DbWriteBase(DbReadBase):
for obj_type, ohandle in self.find_backlink_handles(handle):
obj = self.method("get_%s_from_handle", obj_type)(ohandle)
obj.remove_handle_references('Person', [handle])
obj.remove_handle_references("Person", [handle])
self.method("commit_%s", obj_type)(obj, trans)
self.remove_person(handle, trans)
@@ -1918,24 +1929,23 @@ class DbWriteBase(DbReadBase):
for obj_type, ohandle in self.find_backlink_handles(family_handle):
obj = self.method("get_%s_from_handle", obj_type)(ohandle)
if obj:
obj.remove_handle_references('Family', [family_handle])
obj.remove_handle_references("Family", [family_handle])
self.method("commit_%s", obj_type)(obj, trans)
self.remove_family(family_handle, trans)
def remove_parent_from_family(self, person_handle, family_handle,
trans=None):
def remove_parent_from_family(self, person_handle, family_handle, trans=None):
"""
Remove a person as either the father or mother of a family,
deleting the family if it becomes empty.
"""
if trans is None:
with DbTxn('', self) as trans:
msg = self.__remove_parent_from_family(person_handle,
family_handle, trans)
with DbTxn("", self) as trans:
msg = self.__remove_parent_from_family(
person_handle, family_handle, trans
)
trans.set_description(msg)
else:
msg = self.__remove_parent_from_family(person_handle,
family_handle, trans)
msg = self.__remove_parent_from_family(person_handle, family_handle, trans)
trans.set_description(msg)
def __remove_parent_from_family(self, person_handle, family_handle, trans):
@@ -1954,13 +1964,18 @@ class DbWriteBase(DbReadBase):
msg = _("Remove mother from family")
family.set_mother_handle(None)
else:
raise DbTransactionCancel("The relation between the person and "
raise DbTransactionCancel(
"The relation between the person and "
"the family you try to remove is not consistent, please fix "
"that first, for example from the family editor or by running "
"the database repair tool, before removing the family.")
"the database repair tool, before removing the family."
)
if (not family.get_father_handle() and not family.get_mother_handle()
and not family.get_child_ref_list()):
if (
not family.get_father_handle()
and not family.get_mother_handle()
and not family.get_child_ref_list()
):
self.remove_family_relationships(family_handle, trans)
else:
self.commit_family(family, trans)
@@ -1992,8 +2007,18 @@ class DbWriteBase(DbReadBase):
note_len = self.get_number_of_notes()
tag_len = self.get_number_of_tags()
return (person_len + family_len + event_len + place_len + repo_len +
source_len + citation_len + media_len + note_len + tag_len)
return (
person_len
+ family_len
+ event_len
+ place_len
+ repo_len
+ source_len
+ citation_len
+ media_len
+ note_len
+ tag_len
)
def set_birth_death_index(self, person):
"""
@@ -2004,13 +2029,17 @@ class DbWriteBase(DbReadBase):
event_ref_list = person.get_event_ref_list()
for index, ref in enumerate(event_ref_list):
event = self.get_event_from_handle(ref.ref)
if (event.type.is_birth()
if (
event.type.is_birth()
and ref.role.is_primary()
and (birth_ref_index == -1)):
and (birth_ref_index == -1)
):
birth_ref_index = index
elif (event.type.is_death()
elif (
event.type.is_death()
and ref.role.is_primary()
and (death_ref_index == -1)):
and (death_ref_index == -1)
):
death_ref_index = index
person.birth_ref_index = birth_ref_index

View File

@@ -28,15 +28,41 @@ Declare constants used by database modules
# constants
#
# -------------------------------------------------------------------------
__all__ = ( 'DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO',
'DBEXT', 'DBMODE_R', 'DBMODE_W', 'DBUNDOFN', 'DBLOCKFN',
'DBRECOVFN','BDBVERSFN', 'DBLOGNAME', 'SCHVERSFN', 'PCKVERSFN',
'DBBACKEND',
'PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'CITATION_KEY',
'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY',
'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY',
'TXNADD', 'TXNUPD', 'TXNDEL',
"CLASS_TO_KEY_MAP", "KEY_TO_CLASS_MAP", "KEY_TO_NAME_MAP"
__all__ = (
"DBPAGE",
"DBMODE",
"DBCACHE",
"DBLOCKS",
"DBOBJECTS",
"DBUNDO",
"DBEXT",
"DBMODE_R",
"DBMODE_W",
"DBUNDOFN",
"DBLOCKFN",
"DBRECOVFN",
"BDBVERSFN",
"DBLOGNAME",
"SCHVERSFN",
"PCKVERSFN",
"DBBACKEND",
"PERSON_KEY",
"FAMILY_KEY",
"SOURCE_KEY",
"CITATION_KEY",
"EVENT_KEY",
"MEDIA_KEY",
"PLACE_KEY",
"REPOSITORY_KEY",
"NOTE_KEY",
"REFERENCE_KEY",
"TAG_KEY",
"TXNADD",
"TXNUPD",
"TXNDEL",
"CLASS_TO_KEY_MAP",
"KEY_TO_CLASS_MAP",
"KEY_TO_NAME_MAP",
)
DBEXT = ".db" # File extension to be used for database files
@@ -72,7 +98,8 @@ CITATION_KEY = 10
TXNADD, TXNUPD, TXNDEL = 0, 1, 2
CLASS_TO_KEY_MAP = {"Person": PERSON_KEY,
CLASS_TO_KEY_MAP = {
"Person": PERSON_KEY,
"Family": FAMILY_KEY,
"Source": SOURCE_KEY,
"Citation": CITATION_KEY,
@@ -81,9 +108,11 @@ CLASS_TO_KEY_MAP = {"Person": PERSON_KEY,
"Place": PLACE_KEY,
"Repository": REPOSITORY_KEY,
"Note": NOTE_KEY,
"Tag": TAG_KEY}
"Tag": TAG_KEY,
}
KEY_TO_CLASS_MAP = {PERSON_KEY: "Person",
KEY_TO_CLASS_MAP = {
PERSON_KEY: "Person",
FAMILY_KEY: "Family",
SOURCE_KEY: "Source",
CITATION_KEY: "Citation",
@@ -92,16 +121,19 @@ KEY_TO_CLASS_MAP = {PERSON_KEY: "Person",
PLACE_KEY: "Place",
REPOSITORY_KEY: "Repository",
NOTE_KEY: "Note",
TAG_KEY: "Tag"}
TAG_KEY: "Tag",
}
KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
FAMILY_KEY: 'family',
EVENT_KEY: 'event',
SOURCE_KEY: 'source',
CITATION_KEY: 'citation',
PLACE_KEY: 'place',
MEDIA_KEY: 'media',
REPOSITORY_KEY: 'repository',
KEY_TO_NAME_MAP = {
PERSON_KEY: "person",
FAMILY_KEY: "family",
EVENT_KEY: "event",
SOURCE_KEY: "source",
CITATION_KEY: "citation",
PLACE_KEY: "place",
MEDIA_KEY: "media",
REPOSITORY_KEY: "repository",
# REFERENCE_KEY: 'reference',
NOTE_KEY: 'note',
TAG_KEY: 'tag'}
NOTE_KEY: "note",
TAG_KEY: "tag",
}

View File

@@ -87,6 +87,7 @@ def wrapper(method):
every function in a class. The 'wrapped' method logs the original function
that was called, and where it was called from.
"""
@wraps(method)
def wrapped(*args, **keywargs):
"""
@@ -99,10 +100,16 @@ def wrapper(method):
frame = inspect.currentframe()
c_frame = frame.f_back
c_code = c_frame.f_code
LOG.debug('calling %s.%s()... from file %s, line %s in %s',
class_name, func_name, c_code.co_filename,
c_frame.f_lineno, c_code.co_name)
LOG.debug(
"calling %s.%s()... from file %s, line %s in %s",
class_name,
func_name,
c_code.co_filename,
c_frame.f_lineno,
c_code.co_name,
)
return method(*args, **keywargs)
return wrapped
@@ -111,6 +118,7 @@ class MetaClass(type):
transform class by wrapping it with a diagnostic wrapper (if __debig__ is
not set
"""
def __new__(mcs, class_name, bases, classdict):
"""
When the class this is applied to (DummyDb) is instantiated, each method
@@ -134,6 +142,7 @@ class M_A_M_B(ABCMeta, MetaClass):
See recipe: http://code.activestate.com/recipes/204197-solving-the-
metaclass-conflict/
"""
pass
@@ -142,7 +151,17 @@ class M_A_M_B(ABCMeta, MetaClass):
# class DummyDb
#
# -------------------------------------------------------------------------
class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
class DummyDb(
M_A_M_B(
"NewBaseClass",
(
DbReadBase,
Callback,
object,
),
{},
)
):
"""
Gramps database object. This object is a dummy database class that is always
empty and is read-only.
@@ -469,7 +488,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_family_handles(self, sort_handles=False, locale=glocale):
"""
@@ -611,7 +630,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_note_handles(self):
"""
@@ -731,7 +750,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_person_attribute_types(self):
"""
@@ -778,7 +797,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_person_handles(self, sort_handles=False, locale=glocale):
"""
@@ -839,7 +858,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_place_handles(self, sort_handles=False, locale=glocale):
"""
@@ -862,7 +881,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_family_data(self, handle):
"""
@@ -871,7 +890,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_note_data(self, handle):
"""
@@ -880,7 +899,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_media_data(self, handle):
"""
@@ -889,7 +908,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_person_data(self, handle):
"""
@@ -898,7 +917,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_place_data(self, handle):
"""
@@ -907,7 +926,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_repository_data(self, handle):
"""
@@ -916,7 +935,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_source_data(self, handle):
"""
@@ -925,7 +944,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_citation_data(self, handle):
"""
@@ -934,7 +953,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_raw_tag_data(self, handle):
"""
@@ -943,7 +962,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_repo_bookmarks(self):
"""
@@ -981,7 +1000,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_repository_handles(self):
"""
@@ -1052,7 +1071,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_source_handles(self, sort_handles=False, locale=glocale):
"""
@@ -1113,7 +1132,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_citation_handles(self, sort_handles=False, locale=glocale):
"""
@@ -1154,7 +1173,7 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
if not self.db_is_open:
LOG.debug("database is closed")
LOG.warning("handle %s does not exist in the dummy database", handle)
raise HandleError('Handle %s not found' % handle)
raise HandleError("Handle %s not found" % handle)
def get_tag_from_name(self, val):
"""
@@ -1437,11 +1456,17 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
LOG.debug("database is closed")
return []
def load(self, name, callback=None, mode=None, force_schema_upgrade=False,
def load(
self,
name,
callback=None,
mode=None,
force_schema_upgrade=False,
force_bsddb_upgrade=False,
force_bsddb_downgrade=False,
force_python_upgrade=False,
update=True):
update=True,
):
"""
Open the specified database.
"""
@@ -1536,8 +1561,9 @@ class DummyDb(M_A_M_B("NewBaseClass", (DbReadBase, Callback, object,), {})):
"""
LOG.warning("database is readonly")
def set_prefixes(self, person, media, family, source, citation,
place, event, repository, note):
def set_prefixes(
self, person, media, family, source, citation, place, event, repository, note
):
"""
Set the prefixes for the gramps ids for all gramps objects
"""

View File

@@ -27,6 +27,7 @@
# -------------------------------------------------------------------------
from ..const import URL_WIKISTRING, URL_MANUAL_PAGE
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -35,13 +36,13 @@ _ = glocale.translation.gettext
#
# -------------------------------------------------------------------------
URL_WIKI_START = '<a href="' + URL_WIKISTRING
URL_MANUAL_START = URL_WIKI_START + URL_MANUAL_PAGE + '_-_Manage_Family_Trees'
URL_MANUAL_START = URL_WIKI_START + URL_MANUAL_PAGE + "_-_Manage_Family_Trees"
URL_BACKUP1_START = URL_WIKI_START + 'How_to_make_a_backup">'
URL_BACKUP2_START = URL_MANUAL_START + '#Backing_up_a_Family_Tree">'
URL_EXPORT_START = URL_MANUAL_START + '#Export_into_Gramps_formats">'
class DbException(Exception):
class DbException(Exception):
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -49,10 +50,12 @@ class DbException(Exception):
def __str__(self):
return self.value
class DbWriteFailure(Exception):
"""
Error used to indicate that a write to a database has failed.
"""
def __init__(self, value, value2=""):
Exception.__init__(self)
self.value = value
@@ -64,11 +67,13 @@ class DbWriteFailure(Exception):
def messages(self):
return self.value, self.value2
class DbTransactionCancel(Exception):
"""
Error used to indicate that a transaction needs to be canceled,
for example becuase it is lengthy and the users requests so.
"""
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -76,11 +81,13 @@ class DbTransactionCancel(Exception):
def __str__(self):
return self.value
class DbVersionError(Exception):
"""
Error used to report that a file could not be read because it is written
in an unsupported version of the file format.
"""
def __init__(self, tree_vers, min_vers, max_vers):
Exception.__init__(self)
self.tree_vers = tree_vers
@@ -88,22 +95,27 @@ class DbVersionError(Exception):
self.max_vers = max_vers
def __str__(self):
return _('The schema version is not supported by this version of '
'Gramps.\n\n'
'This Family Tree is schema version %(tree_vers)s, and this '
'version of Gramps supports versions %(min_vers)s to '
'%(max_vers)s\n\n'
'Please upgrade to the corresponding version or use '
'XML for porting data between different schema versions.') %\
{'tree_vers': self.tree_vers,
'min_vers': self.min_vers,
'max_vers': self.max_vers}
return _(
"The schema version is not supported by this version of "
"Gramps.\n\n"
"This Family Tree is schema version %(tree_vers)s, and this "
"version of Gramps supports versions %(min_vers)s to "
"%(max_vers)s\n\n"
"Please upgrade to the corresponding version or use "
"XML for porting data between different schema versions."
) % {
"tree_vers": self.tree_vers,
"min_vers": self.min_vers,
"max_vers": self.max_vers,
}
class DbPythonError(Exception):
"""
Error used to report that a file could not be read because it is written
in an unsupported version of the Python format.
"""
def __init__(self, tree_vers, min_vers, max_vers):
Exception.__init__(self)
self.tree_vers = tree_vers
@@ -111,16 +123,19 @@ class DbPythonError(Exception):
self.max_vers = max_vers
def __str__(self):
return _('The Python version is not supported by this version of '
'Gramps.\n\n'
'This Family Tree is Python version %(tree_vers)s, and this '
'version of Gramps supports versions %(min_vers)s to '
'%(max_vers)s\n\n'
'Please upgrade to the corresponding version or use '
'XML for porting data between different Python versions.') %\
{'tree_vers': self.tree_vers,
'min_vers': self.min_vers,
'max_vers': self.max_vers}
return _(
"The Python version is not supported by this version of "
"Gramps.\n\n"
"This Family Tree is Python version %(tree_vers)s, and this "
"version of Gramps supports versions %(min_vers)s to "
"%(max_vers)s\n\n"
"Please upgrade to the corresponding version or use "
"XML for porting data between different Python versions."
) % {
"tree_vers": self.tree_vers,
"min_vers": self.min_vers,
"max_vers": self.max_vers,
}
class DbUpgradeRequiredError(Exception):
@@ -128,84 +143,94 @@ class DbUpgradeRequiredError(Exception):
Error used to report that a database needs to be upgraded before it can be
used.
"""
def __init__(self, oldschema, newschema):
Exception.__init__(self)
self.oldschema = oldschema
self.newschema = newschema
def __str__(self):
return _('The Family Tree you are trying to load is in the schema '
'version %(oldschema)s format. This version of Gramps uses '
'schema version %(newschema)s. Therefore you cannot load this '
'Family Tree without upgrading the schema version of the '
'Family Tree.\n\n'
'If you upgrade then you won\'t be able to use the previous '
'version of Gramps, even if you subsequently '
'%(wiki_manual_backup_html_start)sbackup%(html_end)s or '
'%(wiki_manual_export_html_start)sexport%(html_end)s '
'your upgraded Family Tree.\n\n'
'Upgrading is a difficult task which could irretrievably '
'corrupt your Family Tree if it is interrupted or fails.\n\n'
'If you have not already made a backup of your Family Tree, '
'then you should start your %(bold_start)sold%(bold_end)s '
'version of Gramps and '
'%(wiki_backup_html_start)smake a backup%(html_end)s '
'of your Family Tree.') % {
'wiki_backup_html_start' : URL_BACKUP1_START ,
'wiki_manual_backup_html_start' : URL_BACKUP2_START ,
'wiki_manual_export_html_start' : URL_EXPORT_START ,
'html_end' : '</a>' ,
'bold_start' : '<b>' ,
'bold_end' : '</b>' ,
'oldschema' : self.oldschema,
'newschema' : self.newschema }
return _(
"The Family Tree you are trying to load is in the schema "
"version %(oldschema)s format. This version of Gramps uses "
"schema version %(newschema)s. Therefore you cannot load this "
"Family Tree without upgrading the schema version of the "
"Family Tree.\n\n"
"If you upgrade then you won't be able to use the previous "
"version of Gramps, even if you subsequently "
"%(wiki_manual_backup_html_start)sbackup%(html_end)s or "
"%(wiki_manual_export_html_start)sexport%(html_end)s "
"your upgraded Family Tree.\n\n"
"Upgrading is a difficult task which could irretrievably "
"corrupt your Family Tree if it is interrupted or fails.\n\n"
"If you have not already made a backup of your Family Tree, "
"then you should start your %(bold_start)sold%(bold_end)s "
"version of Gramps and "
"%(wiki_backup_html_start)smake a backup%(html_end)s "
"of your Family Tree."
) % {
"wiki_backup_html_start": URL_BACKUP1_START,
"wiki_manual_backup_html_start": URL_BACKUP2_START,
"wiki_manual_export_html_start": URL_EXPORT_START,
"html_end": "</a>",
"bold_start": "<b>",
"bold_end": "</b>",
"oldschema": self.oldschema,
"newschema": self.newschema,
}
class DbConnectionError(Exception):
"""
Error used to report that a database connection failed.
"""
def __init__(self, msg, settings_file):
Exception.__init__(self)
self.msg = msg
self.settings_file = settings_file
def __str__(self):
return _('Database connection failed.\n\n'
'%(message)s\n'
'Please check your connection settings file:\n'
'%(settings_file)s') % {
'message': self.msg,
'settings_file': self.settings_file}
return _(
"Database connection failed.\n\n"
"%(message)s\n"
"Please check your connection settings file:\n"
"%(settings_file)s"
) % {"message": self.msg, "settings_file": self.settings_file}
class DbSupportedError(Exception):
"""
Error used to report that a database is no longer supported.
"""
def __init__(self, msg):
Exception.__init__(self)
self.msg = msg
def __str__(self):
return _('The Family Tree you are trying to load is in the %(dbtype)s '
'database, which is no longer supported.\nTherefore you '
'cannot load this Family Tree without upgrading.\n\n'
'If you upgrade then you won\'t be able to use the previous '
'version of Gramps, even if you subsequently '
'%(wiki_manual_backup_html_start)sbackup%(html_end)s or '
'%(wiki_manual_export_html_start)sexport%(html_end)s '
'your upgraded Family Tree.\n\n'
'You are strongly advised to backup your Family Tree.\n\n'
'If you have not already made a backup of your Family Tree, '
'then you should start your previous version of Gramps and '
'%(wiki_backup_html_start)smake a backup%(html_end)s '
'of your Family Tree.') % {
'dbtype' : self.msg,
'wiki_manual_backup_html_start' : URL_BACKUP2_START ,
'wiki_manual_export_html_start' : URL_EXPORT_START ,
'wiki_backup_html_start' : URL_BACKUP1_START ,
'html_end' : '</a>'}
return _(
"The Family Tree you are trying to load is in the %(dbtype)s "
"database, which is no longer supported.\nTherefore you "
"cannot load this Family Tree without upgrading.\n\n"
"If you upgrade then you won't be able to use the previous "
"version of Gramps, even if you subsequently "
"%(wiki_manual_backup_html_start)sbackup%(html_end)s or "
"%(wiki_manual_export_html_start)sexport%(html_end)s "
"your upgraded Family Tree.\n\n"
"You are strongly advised to backup your Family Tree.\n\n"
"If you have not already made a backup of your Family Tree, "
"then you should start your previous version of Gramps and "
"%(wiki_backup_html_start)smake a backup%(html_end)s "
"of your Family Tree."
) % {
"dbtype": self.msg,
"wiki_manual_backup_html_start": URL_BACKUP2_START,
"wiki_manual_export_html_start": URL_EXPORT_START,
"wiki_backup_html_start": URL_BACKUP1_START,
"html_end": "</a>",
}
if __name__ == "__main__":
"""
@@ -215,11 +240,8 @@ if __name__ == "__main__":
"""
import sys
print("\nDbVersionError:\n",
DbVersionError('1.6.0', '1.5.0', '1.5.1'))
print("\nDbUpgradeRequiredError:\n",
DbUpgradeRequiredError('1.5.1', '1.6.0'))
print("\nDbVersionError:\n", DbVersionError("1.6.0", "1.5.0", "1.5.1"))
print("\nDbUpgradeRequiredError:\n", DbUpgradeRequiredError("1.5.1", "1.6.0"))
sys.exit(0)
print("\nxxx:\n",
xxx('4.8.30', '4.8.29'))
print("\nxxx:\n", xxx("4.8.30", "4.8.29"))

File diff suppressed because it is too large Load Diff

View File

@@ -55,8 +55,16 @@ class DbTxn(defaultdict):
Define a group of database commits that define a single logical operation.
"""
__slots__ = ('msg', 'commitdb', 'db', 'batch', 'first',
'last', 'timestamp', '__dict__')
__slots__ = (
"msg",
"commitdb",
"db",
"batch",
"first",
"last",
"timestamp",
"__dict__",
)
def __enter__(self):
"""
@@ -81,10 +89,15 @@ class DbTxn(defaultdict):
frame = inspect.currentframe()
c_frame = frame.f_back
c_code = c_frame.f_code
_LOG.debug(" **** DbTxn %s exited. Called from file %s, "
_LOG.debug(
" **** DbTxn %s exited. Called from file %s, "
"line %s, in %s **** %.2f seconds",
hex(id(self)), c_code.co_filename, c_frame.f_lineno,
c_code.co_name, elapsed_time)
hex(id(self)),
c_code.co_filename,
c_frame.f_lineno,
c_code.co_name,
elapsed_time,
)
return False
@@ -129,16 +142,20 @@ class DbTxn(defaultdict):
# frame to get any real information. The test does not accurately
# check this, but seems to be good enough for the current diagnostic
# purposes.
if os.path.split(caller_frame[1])[1] == "generic.py" and \
caller_frame[3] == "__init__":
if (
os.path.split(caller_frame[1])[1] == "generic.py"
and caller_frame[3] == "__init__"
):
caller_frame = inspect.stack()[2]
_LOG.debug("%sDbTxn %s instantiated for '%s'. Called from file %s, "
"line %s, in %s" %
(("Batch " if batch else "",)+
(hex(id(self)),)+
(msg,)+
(os.path.split(caller_frame[1])[1],)+
(tuple(caller_frame[i] for i in range(2, 4)))
_LOG.debug(
"%sDbTxn %s instantiated for '%s'. Called from file %s, "
"line %s, in %s"
% (
("Batch " if batch else "",)
+ (hex(id(self)),)
+ (msg,)
+ (os.path.split(caller_frame[1])[1],)
+ (tuple(caller_frame[i] for i in range(2, 4)))
)
)
defaultdict.__init__(self, list, {})
@@ -176,12 +193,13 @@ class DbTxn(defaultdict):
data is the tuple returned by the object's serialize method.
"""
self.last = self.commitdb.append(
pickle.dumps((obj_type, trans_type, handle, old_data, new_data), 1))
pickle.dumps((obj_type, trans_type, handle, old_data, new_data), 1)
)
if self.last is None:
self.last = len(self.commitdb) - 1
if self.first is None:
self.first = self.last
_LOG.debug('added to trans: %d %d %s' % (obj_type, trans_type, handle))
_LOG.debug("added to trans: %d %d %s" % (obj_type, trans_type, handle))
self[(obj_type, trans_type)] += [(handle, new_data)]
return

View File

@@ -26,13 +26,14 @@ from abc import ABCMeta, abstractmethod
import time
from collections import deque
class DbUndo(metaclass=ABCMeta):
"""
Base class for the Gramps undo/redo manager. Needs to be subclassed
for use with a real backend.
"""
__slots__ = ('undodb', 'db', 'undo_history_timestamp', 'undoq', 'redoq')
__slots__ = ("undodb", "db", "undo_history_timestamp", "undoq", "redoq")
def __init__(self, db):
"""
@@ -109,13 +110,11 @@ class DbUndo(metaclass=ABCMeta):
@abstractmethod
def _redo(self, update_history):
"""
"""
""" """
@abstractmethod
def _undo(self, update_history):
"""
"""
""" """
def commit(self, txn, msg):
"""

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,7 @@ from ..constfunc import win, get_env_var
from ..config import config
from .dbconst import DBLOGNAME, DBLOCKFN, DBBACKEND
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -52,6 +53,7 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
_LOG = logging.getLogger(DBLOGNAME)
def make_database(plugin_id):
"""
Make a database, given a plugin id.
@@ -73,19 +75,26 @@ def make_database(plugin_id):
db = database()
if __debug__ and _LOG.isEnabledFor(logging.DEBUG):
import inspect
frame = inspect.currentframe()
c_frame = frame.f_back
c_code = c_frame.f_code
_LOG.debug("Database class instance created Class:%s instance:%s. "
_LOG.debug(
"Database class instance created Class:%s instance:%s. "
"Called from File %s, line %s, in %s",
db.__class__.__name__, hex(id(db)), c_code.co_filename,
c_frame.f_lineno, c_code.co_name)
db.__class__.__name__,
hex(id(db)),
c_code.co_filename,
c_frame.f_lineno,
c_code.co_name,
)
return db
else:
raise Exception("can't load database backend: '%s'" % plugin_id)
else:
raise Exception("no such database backend: '%s'" % plugin_id)
def open_database(dbname, force_unlock=False, callback=None):
"""
Open a database by name and return the database.
@@ -99,16 +108,17 @@ def open_database(dbname, force_unlock=False, callback=None):
database.load(dbpath, callback=callback)
return database
def lookup_family_tree(dbname):
"""
Find a Family Tree given its name, and return properties.
"""
dbdir = os.path.expanduser(config.get('database.path'))
dbdir = os.path.expanduser(config.get("database.path"))
for dpath in os.listdir(dbdir):
dirpath = os.path.join(dbdir, dpath)
path_name = os.path.join(dirpath, "name.txt")
if os.path.isfile(path_name):
with open(path_name, 'r', encoding='utf8') as file:
with open(path_name, "r", encoding="utf8") as file:
name = file.readline().strip()
if dbname == name:
locked = False
@@ -116,7 +126,7 @@ def lookup_family_tree(dbname):
backend = get_dbid_from_path(dirpath)
try:
fname = os.path.join(dirpath, "lock")
with open(fname, 'r', encoding='utf8') as ifile:
with open(fname, "r", encoding="utf8") as ifile:
locked_by = ifile.read().strip()
locked = True
except (OSError, IOError):
@@ -124,6 +134,7 @@ def lookup_family_tree(dbname):
return (dirpath, locked, locked_by, backend)
return None
def get_dbid_from_path(dirpath):
"""
Return a database backend from a directory path.
@@ -135,6 +146,7 @@ def get_dbid_from_path(dirpath):
dbid = file.read().strip()
return dbid
def import_as_dict(filename, user, skp_imp_adds=True):
"""
Import the filename into a InMemoryDB and return it.
@@ -143,18 +155,20 @@ def import_as_dict(filename, user, skp_imp_adds=True):
db.load(":memory:")
db.set_feature("skip-import-additions", skp_imp_adds)
db.set_prefixes(
config.get('preferences.iprefix'),
config.get('preferences.oprefix'),
config.get('preferences.fprefix'),
config.get('preferences.sprefix'),
config.get('preferences.cprefix'),
config.get('preferences.pprefix'),
config.get('preferences.eprefix'),
config.get('preferences.rprefix'),
config.get('preferences.nprefix'))
config.get("preferences.iprefix"),
config.get("preferences.oprefix"),
config.get("preferences.fprefix"),
config.get("preferences.sprefix"),
config.get("preferences.cprefix"),
config.get("preferences.pprefix"),
config.get("preferences.eprefix"),
config.get("preferences.rprefix"),
config.get("preferences.nprefix"),
)
status = import_from_filename(db, filename, user)
return db if status else None
def import_from_filename(db, filename, user):
"""
Import a file into a database.
@@ -179,6 +193,7 @@ def import_from_filename(db, filename, user):
return True
return False
def find_surname_name(key, data):
"""
Creating a surname from raw name, to use for sort and index
@@ -186,6 +201,7 @@ def find_surname_name(key, data):
"""
return __index_surname(data[5])
def __index_surname(surn_list):
"""
All non pa/matronymic surnames are used in indexing.
@@ -193,26 +209,36 @@ def __index_surname(surn_list):
returns a byte string
"""
from ..lib import NameOriginType
if surn_list:
surn = " ".join([x[0] for x in surn_list if not (x[3][0] in [
NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC])])
surn = " ".join(
[
x[0]
for x in surn_list
if not (
x[3][0] in [NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]
)
]
)
else:
surn = ""
return surn
def clear_lock_file(name):
try:
os.unlink(os.path.join(name, DBLOCKFN))
except OSError:
return
def write_lock_file(name):
if not os.path.isdir(name):
os.mkdir(name)
with open(os.path.join(name, DBLOCKFN), "w", encoding='utf8') as f:
with open(os.path.join(name, DBLOCKFN), "w", encoding="utf8") as f:
if win():
user = get_env_var('USERNAME')
host = get_env_var('USERDOMAIN')
user = get_env_var("USERNAME")
host = get_env_var("USERDOMAIN")
if not user:
user = _("Unknown")
else:
@@ -223,7 +249,7 @@ def write_lock_file(name):
except:
# not win, so don't need get_env_var.
# under cron getlogin() throws and there is no USER.
user = os.environ.get('USER', 'noUSER')
user = os.environ.get("USER", "noUSER")
if host:
text = "%s@%s" % (user, host)
else:

View File

@@ -52,14 +52,15 @@ from .db.dummydb import DummyDb
LOG = logging.getLogger(".dbstate")
_LOG = logging.getLogger(DBLOGNAME)
class DbState(Callback):
"""
Provide a class to encapsulate the state of the database.
"""
__signals__ = {
'database-changed' : ((DbReadBase, ProxyDbBase), ),
'no-database' : None,
"database-changed": ((DbReadBase, ProxyDbBase),),
"no-database": None,
}
def __init__(self):
@@ -91,9 +92,14 @@ class DbState(Callback):
frame = inspect.currentframe()
c_frame = frame.f_back
c_code = c_frame.f_code
_LOG.debug('calling %s.%s()... from file %s, line %s in %s',
class_name, func_name, c_code.co_filename,
c_frame.f_lineno, c_code.co_name)
_LOG.debug(
"calling %s.%s()... from file %s, line %s in %s",
class_name,
func_name,
c_code.co_filename,
c_frame.f_lineno,
c_code.co_name,
)
return (self.db is not None) and self.db.is_open()
def change_database(self, database):
@@ -102,7 +108,7 @@ class DbState(Callback):
Retained for backward compatibility.
"""
if database:
self.emit('no-database', ())
self.emit("no-database", ())
if self.is_open():
self.db.close()
self.change_database_noclose(database)
@@ -113,33 +119,34 @@ class DbState(Callback):
"""
self.db = database
self.db.set_prefixes(
config.get('preferences.iprefix'),
config.get('preferences.oprefix'),
config.get('preferences.fprefix'),
config.get('preferences.sprefix'),
config.get('preferences.cprefix'),
config.get('preferences.pprefix'),
config.get('preferences.eprefix'),
config.get('preferences.rprefix'),
config.get('preferences.nprefix'))
config.get("preferences.iprefix"),
config.get("preferences.oprefix"),
config.get("preferences.fprefix"),
config.get("preferences.sprefix"),
config.get("preferences.cprefix"),
config.get("preferences.pprefix"),
config.get("preferences.eprefix"),
config.get("preferences.rprefix"),
config.get("preferences.nprefix"),
)
self.open = True
def signal_change(self):
"""
Emits the database-changed signal with the new database
"""
self.emit('database-changed', (self.db, ))
self.emit("database-changed", (self.db,))
def no_database(self):
"""
Closes the database without a new database (except for the DummyDb)
"""
self.emit('no-database', ())
self.emit("no-database", ())
if self.is_open():
self.db.close()
self.db = DummyDb()
self.open = False
self.emit('database-changed', (self.db, ))
self.emit("database-changed", (self.db,))
def get_database(self):
"""
@@ -168,7 +175,7 @@ class DbState(Callback):
"""
self.stack.append(self.db)
self.db = proxy(self.db, *args, **kwargs)
self.emit('database-changed', (self.db, ))
self.emit("database-changed", (self.db,))
def pop_proxy(self):
"""
@@ -180,4 +187,4 @@ class DbState(Callback):
>>> dbstate.pop_proxy()
"""
self.db = self.stack.pop()
self.emit('database-changed', (self.db, ))
self.emit("database-changed", (self.db,))

View File

@@ -18,4 +18,3 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

File diff suppressed because it is too large Load Diff

View File

@@ -36,11 +36,13 @@ import xml.dom.minidom
#
# -------------------------------------------------------------------------
from ..const import PLACE_FORMATS, GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from ..config import config
from ..utils.location import get_location_list
from ..lib import PlaceType
# -------------------------------------------------------------------------
#
# PlaceFormat class
@@ -61,17 +63,16 @@ class PlaceFormat:
#
# -------------------------------------------------------------------------
class PlaceDisplay:
def __init__(self):
self.place_formats = []
self.default_format = config.get('preferences.place-format')
self.default_format = config.get("preferences.place-format")
if os.path.exists(PLACE_FORMATS):
try:
self.load_formats()
return
except BaseException:
print(_("Error in '%s' file: cannot load.") % PLACE_FORMATS)
pf = PlaceFormat(_('Full'), ':', '', 0, False)
pf = PlaceFormat(_("Full"), ":", "", 0, False)
self.place_formats.append(pf)
def display_event(self, db, event, fmt=-1):
@@ -87,11 +88,11 @@ class PlaceDisplay:
def display(self, db, place, date=None, fmt=-1):
if not place:
return ""
if not config.get('preferences.place-auto'):
if not config.get("preferences.place-auto"):
return place.title
else:
if fmt == -1:
fmt = config.get('preferences.place-format')
fmt = config.get("preferences.place-format")
pf = self.place_formats[fmt]
lang = pf.language
all_places = get_location_list(db, place, date, lang)
@@ -99,8 +100,8 @@ class PlaceDisplay:
# Apply format string to place list
index = _find_populated_place(all_places)
places = []
for slice in pf.levels.split(','):
parts = slice.split(':')
for slice in pf.levels.split(","):
parts = slice.split(":")
if len(parts) == 1:
offset = _get_offset(parts[0], index)
if offset is not None:
@@ -126,11 +127,15 @@ class PlaceDisplay:
idx = None
if idx is not None and len(places) > idx + 1:
if pf.street == 1:
combined = (places[idx][0] + ' ' + places[idx+1][0],
places[idx+1][1])
combined = (
places[idx][0] + " " + places[idx + 1][0],
places[idx + 1][1],
)
else:
combined = (places[idx+1][0] + ' ' + places[idx][0],
places[idx+1][1])
combined = (
places[idx + 1][0] + " " + places[idx][0],
places[idx + 1][1],
)
places = places[:idx] + [combined] + places[idx + 2 :]
names = [item[0] for item in places]
@@ -148,14 +153,14 @@ class PlaceDisplay:
def load_formats(self):
dom = xml.dom.minidom.parse(PLACE_FORMATS)
top = dom.getElementsByTagName('place_formats')
top = dom.getElementsByTagName("place_formats")
for fmt in top[0].getElementsByTagName('format'):
name = fmt.attributes['name'].value
levels = fmt.attributes['levels'].value
language = fmt.attributes['language'].value
street = int(fmt.attributes['street'].value)
reverse = fmt.attributes['reverse'].value == 'True'
for fmt in top[0].getElementsByTagName("format"):
name = fmt.attributes["name"].value
levels = fmt.attributes["levels"].value
language = fmt.attributes["language"].value
street = int(fmt.attributes["street"].value)
reverse = fmt.attributes["reverse"].value == "True"
pf = PlaceFormat(name, levels, language, street, reverse)
self.place_formats.append(pf)
@@ -163,22 +168,22 @@ class PlaceDisplay:
def save_formats(self):
doc = xml.dom.minidom.Document()
place_formats = doc.createElement('place_formats')
place_formats = doc.createElement("place_formats")
doc.appendChild(place_formats)
for fmt in self.place_formats:
node = doc.createElement('format')
node = doc.createElement("format")
place_formats.appendChild(node)
node.setAttribute('name', fmt.name)
node.setAttribute('levels', fmt.levels)
node.setAttribute('language', fmt.language)
node.setAttribute('street', str(fmt.street))
node.setAttribute('reverse', str(fmt.reverse))
with open(PLACE_FORMATS, 'w', encoding='utf-8') as f_d:
doc.writexml(f_d, addindent=' ', newl='\n', encoding='utf-8')
node.setAttribute("name", fmt.name)
node.setAttribute("levels", fmt.levels)
node.setAttribute("language", fmt.language)
node.setAttribute("street", str(fmt.street))
node.setAttribute("reverse", str(fmt.reverse))
with open(PLACE_FORMATS, "w", encoding="utf-8") as f_d:
doc.writexml(f_d, addindent=" ", newl="\n", encoding="utf-8")
def _get_offset(value, index):
if index is not None and value.startswith('p'):
if index is not None and value.startswith("p"):
try:
offset = int(value[1:])
except ValueError:
@@ -191,12 +196,18 @@ def _get_offset(value, index):
offset = None
return offset
def _find_populated_place(places):
populated_place = None
for index, item in enumerate(places):
if int(item[1]) in [PlaceType.HAMLET, PlaceType.VILLAGE,
PlaceType.TOWN, PlaceType.CITY]:
if int(item[1]) in [
PlaceType.HAMLET,
PlaceType.VILLAGE,
PlaceType.TOWN,
PlaceType.CITY,
]:
populated_place = index
return populated_place
displayer = PlaceDisplay()

View File

@@ -25,185 +25,231 @@ from gramps.gen.lib import Name
class NameTest(unittest.TestCase):
def setUp(self):
self.name_display = NameDisplay()
def test_get_name_format_for_all_without_default_format(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=False, only_custom=False, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=False, only_custom=False, only_active=False
)
expected_name_format = [
(1, 'Surname, Given Suffix', '%l, %f %s', True),
(2, 'Given Surname Suffix', '%f %l %s', True),
(3, 'Patronymic, Given', '%y, %s %f', False),
(4, 'Given', '%f', True),
(5, 'Main Surnames, Given Patronymic Suffix Prefix', '%1m %2m %o, %f %1y %s %0m', True),
(-2, 'SURNAME, Given Suffix (Call)', 'SURNAME, Given Suffix (Call)', False),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(1, "Surname, Given Suffix", "%l, %f %s", True),
(2, "Given Surname Suffix", "%f %l %s", True),
(3, "Patronymic, Given", "%y, %s %f", False),
(4, "Given", "%f", True),
(
5,
"Main Surnames, Given Patronymic Suffix Prefix",
"%1m %2m %o, %f %1y %s %0m",
True,
),
(-2, "SURNAME, Given Suffix (Call)", "SURNAME, Given Suffix (Call)", False),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_all_active_without_default_format(self):
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
actual_name_format = self.name_display.get_name_format(also_default=False, only_custom=False, only_active=True)
actual_name_format = self.name_display.get_name_format(
also_default=False, only_custom=False, only_active=True
)
expected_name_format = [
(1, 'Surname, Given Suffix', '%l, %f %s', True),
(2, 'Given Surname Suffix', '%f %l %s', True),
(4, 'Given', '%f', True),
(5, 'Main Surnames, Given Patronymic Suffix Prefix', '%1m %2m %o, %f %1y %s %0m', True),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(1, "Surname, Given Suffix", "%l, %f %s", True),
(2, "Given Surname Suffix", "%f %l %s", True),
(4, "Given", "%f", True),
(
5,
"Main Surnames, Given Patronymic Suffix Prefix",
"%1m %2m %o, %f %1y %s %0m",
True,
),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_all_custom_formats_without_default_format(self):
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
actual_name_format = self.name_display.get_name_format(also_default=False, only_custom=True, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=False, only_custom=True, only_active=False
)
expected_name_format = [
(-2, 'SURNAME, Given Suffix (Call)', 'SURNAME, Given Suffix (Call)', False),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(-2, "SURNAME, Given Suffix (Call)", "SURNAME, Given Suffix (Call)", False),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_active_custom_formats_without_default_format(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=False, only_custom=True, only_active=True)
actual_name_format = self.name_display.get_name_format(
also_default=False, only_custom=True, only_active=True
)
expected_name_format = [
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True)
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_all(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=False, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=False, only_active=False
)
expected_name_format = [
(0, 'Default format (defined by Gramps preferences)', '', True),
(1, 'Surname, Given Suffix', '%l, %f %s', True),
(2, 'Given Surname Suffix', '%f %l %s', True),
(3, 'Patronymic, Given', '%y, %s %f', False),
(4, 'Given', '%f', True),
(5, 'Main Surnames, Given Patronymic Suffix Prefix', '%1m %2m %o, %f %1y %s %0m', True),
(-2, 'SURNAME, Given Suffix (Call)', 'SURNAME, Given Suffix (Call)', False),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(0, "Default format (defined by Gramps preferences)", "", True),
(1, "Surname, Given Suffix", "%l, %f %s", True),
(2, "Given Surname Suffix", "%f %l %s", True),
(3, "Patronymic, Given", "%y, %s %f", False),
(4, "Given", "%f", True),
(
5,
"Main Surnames, Given Patronymic Suffix Prefix",
"%1m %2m %o, %f %1y %s %0m",
True,
),
(-2, "SURNAME, Given Suffix (Call)", "SURNAME, Given Suffix (Call)", False),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_all_active(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=False, only_active=True)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=False, only_active=True
)
expected_name_format = [
(0, 'Default format (defined by Gramps preferences)', '', True),
(1, 'Surname, Given Suffix', '%l, %f %s', True),
(2, 'Given Surname Suffix', '%f %l %s', True),
(4, 'Given', '%f', True),
(5, 'Main Surnames, Given Patronymic Suffix Prefix', '%1m %2m %o, %f %1y %s %0m', True),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(0, "Default format (defined by Gramps preferences)", "", True),
(1, "Surname, Given Suffix", "%l, %f %s", True),
(2, "Given Surname Suffix", "%f %l %s", True),
(4, "Given", "%f", True),
(
5,
"Main Surnames, Given Patronymic Suffix Prefix",
"%1m %2m %o, %f %1y %s %0m",
True,
),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_all_custom_formats(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=True, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=True, only_active=False
)
expected_name_format = [
(-2, 'SURNAME, Given Suffix (Call)', 'SURNAME, Given Suffix (Call)', False),
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(-2, "SURNAME, Given Suffix (Call)", "SURNAME, Given Suffix (Call)", False),
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_get_name_format_for_active_custom_formats(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=True, only_active=True)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=True, only_active=True
)
expected_name_format = [
(-1, 'Surname, Name|Common Suffix', 'Surname, Name|Common Suffix', True)
(-1, "Surname, Name|Common Suffix", "Surname, Name|Common Suffix", True)
]
self.assertEqual(expected_name_format, actual_name_format)
def test_clear_custom_formats(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
self.name_display.clear_custom_formats()
actual_name_format = self.name_display.get_name_format(also_default=False, only_custom=True, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=False, only_custom=True, only_active=False
)
self.assertEqual([], actual_name_format)
def test_do_not_clear_default_formats(self):
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_inactive_custom_name_format('SURNAME, Given Suffix (Call)')
self.add_custom_name_format("Surname, Name|Common Suffix")
self.add_inactive_custom_name_format("SURNAME, Given Suffix (Call)")
self.name_display.clear_custom_formats()
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=False, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=False, only_active=False
)
expected_name_format = [
(0, 'Default format (defined by Gramps preferences)', '', True),
(1, 'Surname, Given Suffix', '%l, %f %s', True),
(2, 'Given Surname Suffix', '%f %l %s', True),
(3, 'Patronymic, Given', '%y, %s %f', False),
(4, 'Given', '%f', True),
(5, 'Main Surnames, Given Patronymic Suffix Prefix', '%1m %2m %o, %f %1y %s %0m', True),
(0, "Default format (defined by Gramps preferences)", "", True),
(1, "Surname, Given Suffix", "%l, %f %s", True),
(2, "Given Surname Suffix", "%f %l %s", True),
(3, "Patronymic, Given", "%y, %s %f", False),
(4, "Given", "%f", True),
(
5,
"Main Surnames, Given Patronymic Suffix Prefix",
"%1m %2m %o, %f %1y %s %0m",
True,
),
]
self.assertEqual(expected_name_format, actual_name_format)
def test_set_name_format(self):
standard_formats_overrides = [
(0, "Default", '', True),
(1, "Surname", '%l', True),
(2, "Suffix", '%s', True),
(3, "Given", '%s', False),
(4, "Patronymic", '%y', True),
(5, "Prefix", '%0m', True),
(0, "Default", "", True),
(1, "Surname", "%l", True),
(2, "Suffix", "%s", True),
(3, "Given", "%s", False),
(4, "Patronymic", "%y", True),
(5, "Prefix", "%0m", True),
]
self.name_display.set_name_format(standard_formats_overrides)
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=False, only_active=False)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=False, only_active=False
)
self.assertEqual(standard_formats_overrides, actual_name_format)
def test_add_existing_name_format(self):
self.assertEqual(
self.add_custom_name_format('Surname, Name|Common Suffix'),
self.add_custom_name_format('Surname, Name|Common Suffix')
self.add_custom_name_format("Surname, Name|Common Suffix"),
self.add_custom_name_format("Surname, Name|Common Suffix"),
)
def test_delete_name_format(self):
index = self.add_custom_name_format('Surname, Name|Common Suffix')
index = self.add_custom_name_format("Surname, Name|Common Suffix")
self.name_display.del_name_format(index)
actual_name_format = self.name_display.get_name_format(also_default=True, only_custom=True, only_active=True)
actual_name_format = self.name_display.get_name_format(
also_default=True, only_custom=True, only_active=True
)
self.assertEqual([], actual_name_format)
def test_set_default_format_to_custom_format(self):
index = self.add_custom_name_format('Surname, Name|Common Suffix')
index = self.add_custom_name_format("Surname, Name|Common Suffix")
self.name_display.set_default_format(index)
self.assertEqual(index, self.name_display.get_default_format())
@@ -214,26 +260,26 @@ class NameTest(unittest.TestCase):
def test_display_name_with_valid_format(self):
name = Name()
name.set_first_name('William')
name.set_call_name('Will')
name.set_first_name("William")
name.set_call_name("Will")
name.set_display_as(4)
actual_display_name = self.name_display.display_name(name)
self.assertEqual('William', actual_display_name)
self.assertEqual("William", actual_display_name)
def test_display_name_with_invalid_format(self):
name = Name()
name.set_first_name('William')
name.set_call_name('Will')
name.set_first_name("William")
name.set_call_name("Will")
name.set_display_as(-100)
display_name = self.name_display.display_name(name)
self.assertEqual(', William', display_name)
self.assertEqual(", William", display_name)
def test_display_no_name(self):
self.assertEqual('', self.name_display.display_name(None))
self.assertEqual("", self.name_display.display_name(None))
def add_custom_name_format(self, name_format):
return self.name_display.add_name_format(name_format, name_format)
@@ -243,5 +289,5 @@ class NameTest(unittest.TestCase):
self.name_display.set_format_inactive(index)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()

View File

@@ -22,8 +22,10 @@
Provide Error objects
"""
class FilterError(Exception):
"""Error used to report Filter errors"""
def __init__(self, value, value2=""):
Exception.__init__(self)
self.value = value
@@ -37,12 +39,14 @@ class FilterError(Exception):
"Return the messages"
return (self.value, self.value2)
class DateError(Exception):
"""Error used to report Date errors
Might have a .date attribute holding an invalid Date object
that triggered the error.
"""
def __init__(self, value=""):
Exception.__init__(self)
self.value = value
@@ -51,8 +55,10 @@ class DateError(Exception):
"Return string representation"
return self.value
class DatabaseError(Exception):
"""Error used to report database errors"""
def __init__(self, value=""):
Exception.__init__(self)
self.value = value
@@ -61,8 +67,10 @@ class DatabaseError(Exception):
"Return string representation"
return self.value
class ReportError(Exception):
"""Error used to report Report errors."""
def __init__(self, value, value2=""):
Exception.__init__(self)
self.value = value
@@ -76,8 +84,10 @@ class ReportError(Exception):
"Return the messages"
return (self.value, self.value2)
class GedcomError(Exception):
"""Error used to report GEDCOM errors"""
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -86,8 +96,10 @@ class GedcomError(Exception):
"Return string representation"
return self.value
class GrampsImportError(Exception):
"""Error used to report mistakes during import of files into Gramps"""
def __init__(self, value, value2=""):
Exception.__init__(self)
self.value = value
@@ -101,8 +113,10 @@ class GrampsImportError(Exception):
"Return the messages"
return (self.value, self.value2)
class PluginError(Exception):
"""Error used to report plugin errors"""
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -111,8 +125,10 @@ class PluginError(Exception):
"Return string representation"
return self.value
class HandleError(Exception):
"""Error used to report wrong database handle errors"""
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -121,8 +137,10 @@ class HandleError(Exception):
"Return string representation"
return self.value
class WindowActiveError(Exception):
"""Error used to report that the request window is already displayed."""
def __init__(self, value):
Exception.__init__(self)
self.value = value
@@ -131,6 +149,7 @@ class WindowActiveError(Exception):
"Return string representation"
return self.value
class UnavailableError(Exception):
def __init__(self, value):
Exception.__init__(self)
@@ -140,14 +159,18 @@ class UnavailableError(Exception):
"Return string representation"
return self.value
class MaskError(Exception):
pass
class ValidationError(Exception):
pass
class DbError(Exception):
"""Error used to report BerkeleyDB errors."""
def __init__(self, value):
Exception.__init__(self)
try:
@@ -160,8 +183,10 @@ class DbError(Exception):
"Return string representation"
return self.value
class MergeError(Exception):
"""Error used to report merge errors"""
def __init__(self, value=""):
Exception.__init__(self)
self.value = value

View File

@@ -26,15 +26,21 @@ CustomFilters = None
from ..const import CUSTOM_FILTERS
from ._filterlist import FilterList
from ._genericfilter import (GenericFilter, GenericFilterFactory,
DeferredFilter, DeferredFamilyFilter)
from ._genericfilter import (
GenericFilter,
GenericFilterFactory,
DeferredFilter,
DeferredFamilyFilter,
)
from ._paramfilter import ParamFilter
from ._searchfilter import SearchFilter, ExactSearchFilter
def reload_custom_filters():
global CustomFilters
CustomFilters = FilterList(CUSTOM_FILTERS)
CustomFilters.load()
# if not CustomFilters: # moved to viewmanager
# reload_custom_filters()

View File

@@ -39,6 +39,8 @@ from ..plug import BasePluginManager
from ..const import GRAMPS_LOCALE as glocale
PLUGMAN = BasePluginManager.get_instance()
# -------------------------------------------------------------------------
#
# FilterList
@@ -55,17 +57,16 @@ class FilterList:
self.file = os.path.expanduser(file)
self._cached = {}
def get_filters_dict(self, namespace='generic'):
def get_filters_dict(self, namespace="generic"):
"""
This runs every for every item to be matched!
"""
if self._cached.get(namespace, None) is None:
filters = self.get_filters(namespace)
self._cached[namespace] = dict([(filt.name, filt) for filt
in filters])
self._cached[namespace] = dict([(filt.name, filt) for filt in filters])
return self._cached[namespace]
def get_filters(self, namespace='generic'):
def get_filters(self, namespace="generic"):
"""
This runs every for every item to be matched!
"""
@@ -73,7 +74,7 @@ class FilterList:
filters = self.filter_namespaces[namespace]
else:
filters = []
plugins = PLUGMAN.process_plugin_data('Filters')
plugins = PLUGMAN.process_plugin_data("Filters")
if plugins:
plugin_filters = []
try:
@@ -88,6 +89,7 @@ class FilterList:
plugin_filters.append(plug)
except:
import traceback
traceback.print_exc()
filters += plugin_filters
return filters
@@ -106,7 +108,7 @@ class FilterList:
if os.path.isfile(self.file):
parser = make_parser()
parser.setContentHandler(FilterParser(self))
with open(self.file, 'r', encoding='utf8') as the_file:
with open(self.file, "r", encoding="utf8") as the_file:
parser.parse(the_file)
except (IOError, OSError):
print("IO/OSError in _filterlist.py")
@@ -116,23 +118,24 @@ class FilterList:
def fix(self, line):
"""sanitize the custom filter name, if needed"""
new_line = line.strip()
new_line = new_line.replace('&', '&amp;')
new_line = new_line.replace('>', '&gt;')
new_line = new_line.replace('<', '&lt;')
return new_line.replace('"', '&quot;')
new_line = new_line.replace("&", "&amp;")
new_line = new_line.replace(">", "&gt;")
new_line = new_line.replace("<", "&lt;")
return new_line.replace('"', "&quot;")
def save(self):
"""save the list of custom filters"""
with open(self.file, 'w', encoding='utf8') as file:
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
file.write('<filters>\n')
with open(self.file, "w", encoding="utf8") as file:
file.write('<?xml version="1.0" encoding="utf-8"?>\n')
file.write("<filters>\n")
for namespace in self.filter_namespaces:
file.write(' <object type="%s">\n' % namespace)
filter_list = self.filter_namespaces[namespace]
sorted_filters = sorted([(filter.get_name(), filter)
for filter in filter_list],
key=lambda x: glocale.sort_key(x[0]))
for (name, the_filter) in sorted_filters: # enable a diff
sorted_filters = sorted(
[(filter.get_name(), filter) for filter in filter_list],
key=lambda x: glocale.sort_key(x[0]),
)
for name, the_filter in sorted_filters: # enable a diff
file.write(' <filter name="%s"' % self.fix(name))
file.write(' function="%s"' % the_filter.get_logical_op())
if the_filter.invert:
@@ -140,15 +143,18 @@ class FilterList:
comment = the_filter.get_comment()
if comment:
file.write(' comment="%s"' % self.fix(comment))
file.write('>\n')
file.write(">\n")
for rule in the_filter.get_rules():
file.write(' <rule class="%s" use_regex="%s" use_case="%s">'
'\n' % (rule.__class__.__name__,
rule.use_regex, rule.use_case))
file.write(
' <rule class="%s" use_regex="%s" use_case="%s">'
"\n"
% (rule.__class__.__name__, rule.use_regex, rule.use_case)
)
for value in list(rule.values()):
file.write(' <arg value="%s"/>'
'\n' % self.fix(value))
file.write(' </rule>\n')
file.write(' </filter>\n')
file.write(' </object>\n')
file.write('</filters>\n')
file.write(
' <arg value="%s"/>' "\n" % self.fix(value)
)
file.write(" </rule>\n")
file.write(" </filter>\n")
file.write(" </object>\n")
file.write("</filters>\n")

View File

@@ -25,6 +25,7 @@
# -------------------------------------------------------------------------
from xml.sax import handler
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -35,6 +36,7 @@ _ = glocale.translation.gettext
from ._genericfilter import GenericFilterFactory
from . import rules
# -------------------------------------------------------------------------
#
# FilterParser
@@ -50,7 +52,7 @@ class FilterParser(handler.ContentHandler):
self.r = None
self.a = []
self.cname = None
self.namespace = 'Person'
self.namespace = "Person"
self.use_regex = False
self.use_case = False
@@ -59,86 +61,101 @@ class FilterParser(handler.ContentHandler):
def startElement(self, tag, attrs):
if tag == "object":
if 'type' in attrs:
self.namespace = attrs['type']
if "type" in attrs:
self.namespace = attrs["type"]
else:
self.namespace = "generic"
if self.namespace == 'MediaObject':
if self.namespace == "MediaObject":
# deals with older custom filters
self.namespace = 'Media'
self.namespace = "Media"
elif tag == "filter":
self.f = GenericFilterFactory(self.namespace)()
self.f.set_name(attrs['name'])
if 'function' in attrs:
self.f.set_name(attrs["name"])
if "function" in attrs:
try:
if int(attrs['function']):
op = 'or'
if int(attrs["function"]):
op = "or"
else:
op = 'and'
op = "and"
except ValueError:
op = attrs['function']
op = attrs["function"]
self.f.set_logical_op(op)
if 'invert' in attrs:
self.f.set_invert(attrs['invert'])
if 'comment' in attrs:
self.f.set_comment(attrs['comment'])
if "invert" in attrs:
self.f.set_invert(attrs["invert"])
if "comment" in attrs:
self.f.set_comment(attrs["comment"])
self.gfilter_list.add(self.namespace, self.f)
elif tag == "rule":
if 'use_regex' in attrs:
self.use_regex = attrs['use_regex'] == 'True'
if "use_regex" in attrs:
self.use_regex = attrs["use_regex"] == "True"
else:
self.use_regex = False
if 'use_case' in attrs:
self.use_case = attrs['use_case'] == 'True'
if "use_case" in attrs:
self.use_case = attrs["use_case"] == "True"
else:
self.use_case = False
save_name = attrs['class']
save_name = attrs["class"]
if save_name in old_names_2_class:
self.r = old_names_2_class[save_name]
else:
try:
# First try to use fully qualified name
exec('self.r = %s' % save_name)
exec("self.r = %s" % save_name)
except (ImportError, NameError, AttributeError):
# Now try to use name from rules.namespace
mc_match = save_name.split('.')
mc_match = save_name.split(".")
last_name = mc_match[-1]
try:
exec('self.r = rules.%s.%s' % (
self.namespace.lower(), last_name))
exec(
"self.r = rules.%s.%s" % (self.namespace.lower(), last_name)
)
except (ImportError, NameError, AttributeError):
print("ERROR: Filter rule '%s' in "\
"filter '%s' not found!"\
% (save_name, self.f.get_name()))
print(
"ERROR: Filter rule '%s' in "
"filter '%s' not found!" % (save_name, self.f.get_name())
)
self.r = None
return
self.a = []
elif tag == "arg":
self.a.append(attrs['value'])
self.a.append(attrs["value"])
def endElement(self, tag):
if tag == "rule" and self.r is not None:
if len(self.r.labels) != len(self.a):
self.__upgrade()
if len(self.r.labels) < len(self.a):
print(_("WARNING: Too many arguments in filter '%s'!\n"\
"Trying to load with subset of arguments.") %\
self.f.get_name())
print(
_(
"WARNING: Too many arguments in filter '%s'!\n"
"Trying to load with subset of arguments."
)
% self.f.get_name()
)
nargs = len(self.r.labels)
rule = self.r(self.a[0:nargs], self.use_regex, self.use_case)
self.f.add_rule(rule)
else:
if len(self.r.labels) > len(self.a):
print(_("WARNING: Too few arguments in filter '%s'!\n" \
" Trying to load anyway in the hope this "\
"will be upgraded.") %\
self.f.get_name())
print(
_(
"WARNING: Too few arguments in filter '%s'!\n"
" Trying to load anyway in the hope this "
"will be upgraded."
)
% self.f.get_name()
)
try:
rule = self.r(self.a, self.use_regex, self.use_case)
except AssertionError as msg:
print(msg)
print(_("ERROR: filter %s could not be correctly loaded. "
"Edit the filter!") % self.f.get_name())
print(
_(
"ERROR: filter %s could not be correctly loaded. "
"Edit the filter!"
)
% self.f.get_name()
)
return
self.f.add_rule(rule)
@@ -152,31 +169,43 @@ class FilterParser(handler.ContentHandler):
"""
# HasPlace rule has extra locality field in v3.3
if self.r == rules.place.HasPlace and len(self.a) == 8:
self.a = self.a[0:2] + [''] + self.a[4:8] + [self.a[3]] + \
[self.a[2]]
self.a = self.a[0:2] + [""] + self.a[4:8] + [self.a[3]] + [self.a[2]]
# HasNameOf rule has new fields for surnames in v3.3
if self.r == rules.person.HasNameOf and len(self.a) == 7:
self.a = self.a[0:2] + [self.a[3]] + [self.a[2]] + [self.a[6]] + \
[''] + [self.a[4]] + ['', ''] + [self.a[5]] + \
['', '0']
self.a = (
self.a[0:2]
+ [self.a[3]]
+ [self.a[2]]
+ [self.a[6]]
+ [""]
+ [self.a[4]]
+ ["", ""]
+ [self.a[5]]
+ ["", "0"]
)
# New regular expression code in v3.4
if (self.r in (rules.person.HasNameOf,
if (
self.r
in (
rules.person.HasNameOf,
rules.family.FatherHasNameOf,
rules.family.MotherHasNameOf,
rules.family.ChildHasNameOf)
and len(self.a) == 12):
self.use_regex = self.a[11] == '1'
rules.family.ChildHasNameOf,
)
and len(self.a) == 12
):
self.use_regex = self.a[11] == "1"
self.a = self.a[:11]
if (self.r == rules.person.HasTextMatchingSubstringOf
and len(self.a) == 3):
self.use_regex = self.a[2] == '1'
if self.r == rules.person.HasTextMatchingSubstringOf and len(self.a) == 3:
self.use_regex = self.a[2] == "1"
self.a = self.a[:2]
# HasEvent rule has extra primary role field in v3.4.7
if self.r == rules.person.HasEvent and len(self.a) == 5:
self.a.append('1')
self.a.append("1")
# IsEnclosedBy rule has extra inclusive field in v4.2.4
if self.r == rules.place.IsEnclosedBy and len(self.a) == 1:
self.a.append('0')
self.a.append("0")
# -------------------------------------------------------------------------
#
@@ -197,23 +226,17 @@ old_names_2_class = {
"Is a descendant of": rules.person.IsDescendantOf,
"Is a descendant family member of": rules.person.IsDescendantFamilyOf,
"Is a descendant of filter match": rules.person.IsDescendantOfFilterMatch,
"Is a descendant of person not more than N generations away":
rules.person.IsLessThanNthGenerationDescendantOf,
"Is a descendant of person at least N generations away":
rules.person.IsMoreThanNthGenerationDescendantOf,
"Is an descendant of person at least N generations away" :
rules.person.IsMoreThanNthGenerationDescendantOf,
"Is a descendant of person not more than N generations away": rules.person.IsLessThanNthGenerationDescendantOf,
"Is a descendant of person at least N generations away": rules.person.IsMoreThanNthGenerationDescendantOf,
"Is an descendant of person at least N generations away": rules.person.IsMoreThanNthGenerationDescendantOf,
"Is a child of filter match": rules.person.IsChildOfFilterMatch,
"Is an ancestor of": rules.person.IsAncestorOf,
"Is an ancestor of filter match": rules.person.IsAncestorOfFilterMatch,
"Is an ancestor of person not more than N generations away" :
rules.person.IsLessThanNthGenerationAncestorOf,
"Is an ancestor of person at least N generations away":
rules.person.IsMoreThanNthGenerationAncestorOf,
"Is an ancestor of person not more than N generations away": rules.person.IsLessThanNthGenerationAncestorOf,
"Is an ancestor of person at least N generations away": rules.person.IsMoreThanNthGenerationAncestorOf,
"Is a parent of filter match": rules.person.IsParentOfFilterMatch,
"Has a common ancestor with": rules.person.HasCommonAncestorWith,
"Has a common ancestor with filter match" :
rules.person.HasCommonAncestorWithFilterMatch,
"Has a common ancestor with filter match": rules.person.HasCommonAncestorWithFilterMatch,
"Is a female": rules.person.IsFemale,
"Is a male": rules.person.IsMale,
"Has the personal event": rules.person.HasEvent,
@@ -224,10 +247,8 @@ old_names_2_class = {
"Matches the filter named": rules.person.HasSourceOf,
"Is spouse of filter match": rules.person.IsSpouseOfFilterMatch,
"Is a sibling of filter match": rules.person.IsSiblingOfFilterMatch,
"Relationship path between two people" :
rules.person.RelationshipPathBetween,
"Relationship paths between a person and a list of people" :
rules.person.DeepRelationshipPathBetween,
"Relationship path between two people": rules.person.RelationshipPathBetween,
"Relationship paths between a person and a list of people": rules.person.DeepRelationshipPathBetween,
"People who were adopted": rules.person.HaveAltFamilies,
"People who have images": rules.person.HavePhotos,
"People with children": rules.person.HaveChildren,

View File

@@ -40,8 +40,10 @@ from ..lib.media import Media
from ..lib.note import Note
from ..lib.tag import Tag
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
#
# GenericFilter
@@ -50,7 +52,7 @@ _ = glocale.translation.gettext
class GenericFilter:
"""Filter class that consists of several rules."""
logical_functions = ['or', 'and', 'xor', 'one']
logical_functions = ["or", "and", "xor", "one"]
def __init__(self, source=None):
if source:
@@ -63,9 +65,9 @@ class GenericFilter:
else:
self.need_param = 0
self.flist = []
self.name = ''
self.comment = ''
self.logical_op = 'and'
self.name = ""
self.comment = ""
self.logical_op = "and"
self.invert = False
def match(self, handle, db):
@@ -78,15 +80,15 @@ class GenericFilter:
return False
def is_empty(self):
return ((len(self.flist) == 0) or
(len(self.flist) == 1 and ((self.flist[0].is_empty() and
not self.invert))))
return (len(self.flist) == 0) or (
len(self.flist) == 1 and ((self.flist[0].is_empty() and not self.invert))
)
def set_logical_op(self, val):
if val in GenericFilter.logical_functions:
self.logical_op = val
else:
self.logical_op = 'and'
self.logical_op = "and"
def get_logical_op(self):
return self.logical_op
@@ -136,15 +138,12 @@ class GenericFilter:
def get_number(self, db):
return db.get_number_of_people()
def check_func(self, db, id_list, task, user=None, tupleind=None,
tree=False):
def check_func(self, db, id_list, task, user=None, tupleind=None, tree=False):
final_list = []
if user:
user.begin_progress(_('Filter'), _('Applying ...'),
self.get_number(db))
user.begin_progress(_("Filter"), _("Applying ..."), self.get_number(db))
if id_list is None:
with (self.get_tree_cursor(db) if tree else
self.get_cursor(db)) as cursor:
with self.get_tree_cursor(db) if tree else self.get_cursor(db) as cursor:
for handle, data in cursor:
person = self.make_obj()
person.unserialize(data)
@@ -171,11 +170,9 @@ class GenericFilter:
final_list = []
flist = self.flist
if user:
user.begin_progress(_('Filter'), _('Applying ...'),
self.get_number(db))
user.begin_progress(_("Filter"), _("Applying ..."), self.get_number(db))
if id_list is None:
with (self.get_tree_cursor(db) if tree else
self.get_cursor(db)) as cursor:
with self.get_tree_cursor(db) if tree else self.get_cursor(db) as cursor:
for handle, data in cursor:
person = self.make_obj()
person.unserialize(data)
@@ -201,16 +198,13 @@ class GenericFilter:
return final_list
def check_or(self, db, id_list, user=None, tupleind=None, tree=False):
return self.check_func(db, id_list, self.or_test, user, tupleind,
tree=False)
return self.check_func(db, id_list, self.or_test, user, tupleind, tree=False)
def check_one(self, db, id_list, user=None, tupleind=None, tree=False):
return self.check_func(db, id_list, self.one_test, user, tupleind,
tree=False)
return self.check_func(db, id_list, self.one_test, user, tupleind, tree=False)
def check_xor(self, db, id_list, user=None, tupleind=None, tree=False):
return self.check_func(db, id_list, self.xor_test, user, tupleind,
tree=False)
return self.check_func(db, id_list, self.xor_test, user, tupleind, tree=False)
def xor_test(self, db, person):
test = False
@@ -232,7 +226,7 @@ class GenericFilter:
def get_check_func(self):
try:
m = getattr(self, 'check_' + self.logical_op)
m = getattr(self, "check_" + self.logical_op)
except AttributeError:
m = self.check_and
return m
@@ -265,8 +259,8 @@ class GenericFilter:
rule.requestreset()
return res
class GenericFamilyFilter(GenericFilter):
class GenericFamilyFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -282,8 +276,8 @@ class GenericFamilyFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_families()
class GenericEventFilter(GenericFilter):
class GenericEventFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -299,8 +293,8 @@ class GenericEventFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_events()
class GenericSourceFilter(GenericFilter):
class GenericSourceFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -316,8 +310,8 @@ class GenericSourceFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_sources()
class GenericCitationFilter(GenericFilter):
class GenericCitationFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -336,8 +330,8 @@ class GenericCitationFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_citations()
class GenericPlaceFilter(GenericFilter):
class GenericPlaceFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -356,8 +350,8 @@ class GenericPlaceFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_places()
class GenericMediaFilter(GenericFilter):
class GenericMediaFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -373,8 +367,8 @@ class GenericMediaFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_media()
class GenericRepoFilter(GenericFilter):
class GenericRepoFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -390,8 +384,8 @@ class GenericRepoFilter(GenericFilter):
def get_number(self, db):
return db.get_number_of_repositories()
class GenericNoteFilter(GenericFilter):
class GenericNoteFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
@@ -409,23 +403,23 @@ class GenericNoteFilter(GenericFilter):
def GenericFilterFactory(namespace):
if namespace == 'Person':
if namespace == "Person":
return GenericFilter
elif namespace == 'Family':
elif namespace == "Family":
return GenericFamilyFilter
elif namespace == 'Event':
elif namespace == "Event":
return GenericEventFilter
elif namespace == 'Source':
elif namespace == "Source":
return GenericSourceFilter
elif namespace == 'Citation':
elif namespace == "Citation":
return GenericCitationFilter
elif namespace == 'Place':
elif namespace == "Place":
return GenericPlaceFilter
elif namespace == 'Media':
elif namespace == "Media":
return GenericMediaFilter
elif namespace == 'Repository':
elif namespace == "Repository":
return GenericRepoFilter
elif namespace == 'Note':
elif namespace == "Note":
return GenericNoteFilter
@@ -453,6 +447,7 @@ class DeferredFilter(GenericFilter):
return self._(self.name_pair[0]) % self.name_pair[1]
return self._(self.name_pair[0])
class DeferredFamilyFilter(GenericFamilyFilter):
"""
Filter class allowing for deferred translation of the filter name

View File

@@ -30,13 +30,13 @@ Package providing filtering framework for Gramps.
from ._genericfilter import GenericFilter
from ..errors import FilterError
# -------------------------------------------------------------------------
#
# ParamFilter
#
# -------------------------------------------------------------------------
class ParamFilter(GenericFilter):
def __init__(self, source=None):
GenericFilter.__init__(self, source)
self.need_param = 1
@@ -58,8 +58,9 @@ class ParamFilter(GenericFilter):
rule.set_list(new_list)
for rule in self.flist:
if rule.nrprepare > 0:
raise FilterError('Custom filters can not twice be used' \
' in a parameter filter')
raise FilterError(
"Custom filters can not twice be used" " in a parameter filter"
)
rule.requestprepare(db, user)
result = GenericFilter.apply(self, db, id_list)
for rule in self.flist:

View File

@@ -22,6 +22,7 @@
Package providing filtering framework for Gramps.
"""
class SearchFilter:
def __init__(self, func, text, invert):
self.func = func
@@ -31,10 +32,10 @@ class SearchFilter:
def match(self, handle, db):
return self.invert ^ (self.func(handle).upper().find(self.text) != -1)
class ExactSearchFilter(SearchFilter):
def __init__(self, func, text, invert):
SearchFilter.__init__(self, func, text, invert)
def match(self, handle, db):
return self.invert ^ (self.func(handle).upper() == self.text.strip())

View File

@@ -81,6 +81,7 @@ from ._matcheseventfilterbase import MatchesEventFilterBase
from ._matchessourceconfidencebase import MatchesSourceConfidenceBase
from ._matchessourcefilterbase import MatchesSourceFilterBase
from ._changedsincebase import ChangedSinceBase
# object filters
from . import person
from . import family

View File

@@ -36,8 +36,10 @@ import time
from . import Rule
from ...errors import FilterError
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
#
# ChangedSince
@@ -48,12 +50,14 @@ class ChangedSinceBase(Rule):
Rule that checks for primary objects changed since a specific time.
"""
labels = [ 'Changed after:', 'but before:' ]
name = 'Objects changed after <date time>'
description = "Matches object records changed after a specified " \
"date/time (yyyy-mm-dd hh:mm:ss) or in range, if a second " \
labels = ["Changed after:", "but before:"]
name = "Objects changed after <date time>"
description = (
"Matches object records changed after a specified "
"date/time (yyyy-mm-dd hh:mm:ss) or in range, if a second "
"date/time is given."
category = _('General filters')
)
category = _("General filters")
def add_time(self, date):
if re.search(r"\d.*\s+\d{1,2}:\d{2}:\d{2}", date):
@@ -80,9 +84,13 @@ class ChangedSinceBase(Rule):
except ValueError:
raise FilterError(
_("Wrong format of date-time"),
_("Only date-times in the iso format of yyyy-mm-dd "
_(
"Only date-times in the iso format of yyyy-mm-dd "
"hh:mm:ss, where the time part is optional, are "
"accepted. %s does not satisfy.") % iso_date_time)
"accepted. %s does not satisfy."
)
% iso_date_time,
)
return time_sec
def prepare(self, db, user):

View File

@@ -24,6 +24,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -33,6 +34,7 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
#
# Everyone
@@ -41,9 +43,9 @@ from . import Rule
class Everything(Rule):
"""Match Everyone."""
name = 'Every object'
category = _('General filters')
description = 'Matches every object in the database'
name = "Every object"
category = _("General filters")
description = "Matches every object in the database"
def is_empty(self):
return True

View File

@@ -25,6 +25,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -46,11 +47,10 @@ class HasAttributeBase(Rule):
Rule that checks for an object with a particular attribute.
"""
labels = ['Attribute:', 'Value:']
name = 'Objects with the <attribute>'
description = "Matches objects with the given attribute " \
"of a particular value"
category = _('General filters')
labels = ["Attribute:", "Value:"]
name = "Objects with the <attribute>"
description = "Matches objects with the given attribute " "of a particular value"
category = _("General filters")
allow_regex = True
def apply(self, db, obj):

View File

@@ -25,6 +25,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -35,6 +36,7 @@ _ = glocale.translation.gettext
from ...datehandler import parser
from . import Rule
# -------------------------------------------------------------------------
#
# HasCitation
@@ -46,12 +48,10 @@ class HasCitationBase(Rule):
First parameter is [Volume/page, Date, Confidence]
"""
labels = [ _('Volume/Page:'),
_('Date:'),
_('Confidence:') ]
name = _('Citations matching parameters')
labels = [_("Volume/Page:"), _("Date:"), _("Confidence:")]
name = _("Citations matching parameters")
description = _("Matches citations with particular parameters")
category = _('Citation/source filters')
category = _("Citation/source filters")
allow_regex = True
def prepare(self, db, user):

View File

@@ -24,6 +24,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -37,6 +38,7 @@ from . import Rule
from ...utils.db import get_participant_from_event
from ...display.place import displayer as place_displayer
# -------------------------------------------------------------------------
#
# HasEventBase
@@ -45,15 +47,10 @@ from ...display.place import displayer as place_displayer
class HasEventBase(Rule):
"""Rule that checks for an event with a particular value."""
labels = [ 'Event type:',
'Date:',
'Place:',
'Description:',
'Main Participants:' ]
name = 'Events matching parameters'
labels = ["Event type:", "Date:", "Place:", "Description:", "Main Participants:"]
name = "Events matching parameters"
description = "Matches events with particular parameters"
category = _('Event filters')
category = _("Event filters")
allow_regex = True
def prepare(self, db, user):
@@ -94,8 +91,9 @@ class HasEventBase(Rule):
else:
return False
if not self.match_substring(4,
get_participant_from_event(db, event.get_handle(), all_=True)):
if not self.match_substring(
4, get_participant_from_event(db, event.get_handle(), all_=True)
):
return False
return True

View File

@@ -27,6 +27,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -36,22 +37,23 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
# "People who have images"
# -------------------------------------------------------------------------
class HasGalleryBase(Rule):
"""Objects who have Media Object"""
labels = [ _('Number of instances:'), _('Number must be:')]
name = 'Object with <count> Media references'
labels = [_("Number of instances:"), _("Number must be:")]
name = "Object with <count> Media references"
description = "Matches objects with certain number of items in the gallery"
category = _('General filters')
category = _("General filters")
def prepare(self, db, user):
# things we want to do just once, not for every handle
if self.list[1] == 'less than':
if self.list[1] == "less than":
self.count_type = 0
elif self.list[1] == 'greater than':
elif self.list[1] == "greater than":
self.count_type = 2
else:
self.count_type = 1 # "equal to"

View File

@@ -25,6 +25,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -34,6 +35,7 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
#
# HasIdOf
@@ -42,10 +44,10 @@ from . import Rule
class HasGrampsId(Rule):
"""Rule that checks for an object with a specific Gramps ID."""
labels = [ _('ID:') ]
name = 'Object with <Id>'
labels = [_("ID:")]
name = "Object with <Id>"
description = "Matches objects with a specified Gramps ID"
category = _('General filters')
category = _("General filters")
def apply(self, db, obj):
"""

View File

@@ -27,6 +27,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -37,6 +38,7 @@ _ = glocale.translation.gettext
from . import Rule
# -------------------------------------------------------------------------
#
# HasLDSBase
@@ -45,16 +47,16 @@ from . import Rule
class HasLDSBase(Rule):
"""Rule that checks for object with a LDS event"""
labels = [ _('Number of instances:'), _('Number must be:')]
name = 'Objects with LDS events'
labels = [_("Number of instances:"), _("Number must be:")]
name = "Objects with LDS events"
description = "Matches objects with LDS events"
category = _('General filters')
category = _("General filters")
def prepare(self, db, user):
# things we want to do just once, not for every handle
if self.list[1] == 'less than':
if self.list[1] == "less than":
self.count_type = 0
elif self.list[1] == 'greater than':
elif self.list[1] == "greater than":
self.count_type = 2
else:
self.count_type = 1 # "equal to"

View File

@@ -28,6 +28,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -37,37 +38,37 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
# "Objects having notes"
# -------------------------------------------------------------------------
class HasNoteBase(Rule):
"""Objects having notes"""
labels = [ _('Number of instances:'), _('Number must be:')]
name = 'Object with notes'
labels = [_("Number of instances:"), _("Number must be:")]
name = "Object with notes"
description = "Matches objects that have a certain number of notes"
category = _('General filters')
category = _("General filters")
def __init__(self, arg, use_regex=False, use_case=False):
# Upgrade from pre 3.1 HasNote filter, use defaults that correspond
# Previous filter had 0 arguments
if len(arg) == 0:
Rule.__init__(self, ["0", 'greater than'], use_regex, use_case)
Rule.__init__(self, ["0", "greater than"], use_regex, use_case)
else:
Rule.__init__(self, arg, use_regex, use_case)
def prepare(self, db, user):
# things we want to do just once, not for every handle
if self.list[1] == 'less than':
if self.list[1] == "less than":
self.count_type = 0
elif self.list[1] == 'greater than':
elif self.list[1] == "greater than":
self.count_type = 2
else:
self.count_type = 1 # "equal to"
self.userSelectedCount = int(self.list[0])
def apply(self, db, obj):
count = len(obj.get_note_list())
if self.count_type == 0: # "less than"

View File

@@ -25,6 +25,7 @@
# -------------------------------------------------------------------------
import re
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -34,17 +35,20 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
# Objects having notes that contain a substring or match a regular expression
# -------------------------------------------------------------------------
class HasNoteRegexBase(Rule):
"""Objects having notes containing <text>."""
labels = [ _('Text:')]
name = 'Objects having notes containing <text>'
description = ("Matches objects whose notes contain a substring "
"or match a regular expression")
category = _('General filters')
labels = [_("Text:")]
name = "Objects having notes containing <text>"
description = (
"Matches objects whose notes contain a substring "
"or match a regular expression"
)
category = _("General filters")
allow_regex = True
def apply(self, db, person):

View File

@@ -24,6 +24,7 @@
#
# -------------------------------------------------------------------------
from ...const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
# -------------------------------------------------------------------------
@@ -33,17 +34,17 @@ _ = glocale.translation.gettext
# -------------------------------------------------------------------------
from . import Rule
# -------------------------------------------------------------------------
# "People having notes that contain a substring"
# -------------------------------------------------------------------------
class HasNoteSubstrBase(Rule):
"""People having notes containing <substring>."""
labels = [ _('Substring:')]
name = 'Objects having notes containing <substring>'
description = "Matches objects whose notes contain text matching a " \
"substring"
category = _('General filters')
labels = [_("Substring:")]
name = "Objects having notes containing <substring>"
description = "Matches objects whose notes contain text matching a " "substring"
category = _("General filters")
def apply(self, db, person):
notelist = person.get_note_list()

Some files were not shown because too many files have changed in this diff Show More