From 61ec1c1b48a5072a271e10cdfa5b15cd03511993 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Thu, 14 May 2015 06:31:59 -0400 Subject: [PATCH] Database backends: bsddb, django, and dictionary --- gramps/cli/clidbman.py | 11 +- gramps/gui/dbman.py | 44 +++-- gramps/plugins/database/dictionarydb.py | 20 ++- .../defaults/default_settings.py | 150 ++++++++++++++++++ .../django_support/defaults/sqlite.db | Bin 0 -> 293888 bytes gramps/plugins/database/djangodb.py | 23 ++- 6 files changed, 230 insertions(+), 18 deletions(-) create mode 100644 gramps/plugins/database/django_support/defaults/default_settings.py create mode 100644 gramps/plugins/database/django_support/defaults/sqlite.db diff --git a/gramps/cli/clidbman.py b/gramps/cli/clidbman.py index a4986f615..893a2d84f 100644 --- a/gramps/cli/clidbman.py +++ b/gramps/cli/clidbman.py @@ -225,7 +225,7 @@ class CLIDbManager(object): """ print(_('Import finished...')) - def create_new_db_cli(self, title=None, create_db=True): + def create_new_db_cli(self, title=None, create_db=True, dbid=None): """ Create a new database. """ @@ -244,8 +244,9 @@ class CLIDbManager(object): if create_db: # write the version number into metadata - - newdb = self.dbstate.make_database("bsddb") + if dbid is None: + dbid = "bsddb" + newdb = self.dbstate.make_database(dbid) newdb.write_version(new_path) (tval, last) = time_val(new_path) @@ -254,11 +255,11 @@ class CLIDbManager(object): last, tval, False, "")) return new_path, title - def _create_new_db(self, title=None): + def _create_new_db(self, title=None, dbid=None): """ Create a new database, do extra stuff needed """ - return self.create_new_db_cli(title) + return self.create_new_db_cli(title, dbid=dbid) def import_new_db(self, filename, user): """ diff --git a/gramps/gui/dbman.py b/gramps/gui/dbman.py index b105ac37b..0891170f4 100644 --- a/gramps/gui/dbman.py +++ b/gramps/gui/dbman.py @@ -72,7 +72,7 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale _ = glocale.translation.gettext from gramps.gen.const import URL_WIKISTRING from .user import User -from .dialog import ErrorDialog, QuestionDialog, QuestionDialog2 +from .dialog import ErrorDialog, QuestionDialog, QuestionDialog2, ICON from .pluginmanager import GuiPluginManager from gramps.cli.clidbman import CLIDbManager, NAME_FILE, time_val from .ddtargets import DdTargets @@ -80,7 +80,6 @@ from gramps.gen.recentfiles import rename_filename, remove_filename from .glade import Glade from gramps.gen.db.exceptions import DbException - _RETURN = Gdk.keyval_from_name("Return") _KP_ENTER = Gdk.keyval_from_name("KP_Enter") @@ -104,6 +103,25 @@ ICON_COL = 6 RCS_BUTTON = { True : _('_Extract'), False : _('_Archive') } +class DatabaseDialog(Gtk.MessageDialog): + def __init__(self, parent=None): + Gtk.MessageDialog.__init__(self, + parent, + flags=Gtk.DialogFlags.MODAL, + type=Gtk.MessageType.QUESTION, + ) + self.set_icon(ICON) + self.set_title('') + + self.set_markup('%s' % + _('Database Backend')) + self.format_secondary_text( + _("Please select a database backend type")) + + self.add_button("BSDDB Database (standard)", 1) + self.add_button("Dictionary (in-memory)", 2) + self.add_button("Django Database", 3) + class DbManager(CLIDbManager): """ Database Manager. Opens a database manager window that allows users to @@ -761,19 +779,27 @@ class DbManager(CLIDbManager): message. """ self.new.set_sensitive(False) - try: - self._create_new_db() - except (OSError, IOError) as msg: - DbManager.ERROR(_("Could not create Family Tree"), - str(msg)) + # popup window and ask for dbid types, if more than one + ## FIXME: autoload from plugins + dbid = "bsddb" + d = DatabaseDialog(self.top) + database = d.run() + d.destroy() + if database >= 0: + dbid = {1:"bsddb",2:"dictionarydb",3:"djangodb"}[database] + try: + self._create_new_db(dbid=dbid) + except (OSError, IOError) as msg: + DbManager.ERROR(_("Could not create Family Tree"), + str(msg)) self.new.set_sensitive(True) - def _create_new_db(self, title=None, create_db=True): + def _create_new_db(self, title=None, create_db=True, dbid=None): """ Create a new database, append to model """ new_path, title = self.create_new_db_cli(conv_to_unicode(title, 'utf8'), - create_db) + create_db, dbid) path_name = os.path.join(new_path, NAME_FILE) (tval, last) = time_val(new_path) node = self.model.append(None, [title, new_path, path_name, diff --git a/gramps/plugins/database/dictionarydb.py b/gramps/plugins/database/dictionarydb.py index 647f6b0ee..936d8f534 100644 --- a/gramps/plugins/database/dictionarydb.py +++ b/gramps/plugins/database/dictionarydb.py @@ -21,7 +21,7 @@ #------------------------------------------------------------------------ # -# Gramps Modules +# Python Modules # #------------------------------------------------------------------------ import pickle @@ -29,7 +29,15 @@ import base64 import time import re import os +import logging + +#------------------------------------------------------------------------ +# +# Gramps Modules +# +#------------------------------------------------------------------------ from gramps.gen.db import DbReadBase, DbWriteBase, DbTxn, KEY_TO_NAME_MAP +from gramps.gen.db.dbconst import * from gramps.gen.utils.callback import Callback from gramps.gen.updatecallback import UpdateCallback from gramps.gen.db import (PERSON_KEY, @@ -56,6 +64,8 @@ from gramps.gen.lib.repo import Repository from gramps.gen.lib.note import Note from gramps.gen.lib.tag import Tag +_LOG = logging.getLogger(DBLOGNAME) + class Environment(object): """ Implements the Environment API. @@ -1612,3 +1622,11 @@ class DictionaryDb(DbWriteBase, DbReadBase, UpdateCallback, Callback): def undo(self, update_history=True): pass + + def write_version(self, directory): + """Write files for a newly created DB.""" + versionpath = os.path.join(directory, str(DBBACKEND)) + _LOG.debug("Write database backend file to 'dictionarydb'") + with open(versionpath, "w") as version_file: + version_file.write("dictionarydb") + diff --git a/gramps/plugins/database/django_support/defaults/default_settings.py b/gramps/plugins/database/django_support/defaults/default_settings.py new file mode 100644 index 000000000..fae3ffd06 --- /dev/null +++ b/gramps/plugins/database/django_support/defaults/default_settings.py @@ -0,0 +1,150 @@ +import os +from gramps.gen.const import DATA_DIR + +WEB_DIR = os.path.dirname(os.path.realpath(__file__)) + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +INTERNAL_IPS = ('127.0.0.1',) + +ADMINS = ( + ('admin', 'your_email@domain.com'), +) + +MANAGERS = ADMINS +DATABASE_ROUTERS = [] +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(WEB_DIR, 'sqlite.db'), + } +} +DATABASE_ENGINE = 'sqlite3' +DATABASE_NAME = os.path.join(WEB_DIR, 'sqlite.db') +DATABASE_USER = '' +DATABASE_PASSWORD = '' +DATABASE_HOST = '' +DATABASE_PORT = '' +TIME_ZONE = 'America/New_York' +LANGUAGE_CODE = 'en-us' +SITE_ID = 1 +USE_I18N = True +MEDIA_ROOT = '' +MEDIA_URL = '' +ADMIN_MEDIA_PREFIX = '/gramps-media/' +SECRET_KEY = 'zd@%vslj5sqhx94_8)0hsx*rk9tj3^ly$x+^*tq4bggr&uh$ac' + +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', # 1.4 + 'django.template.loaders.app_directories.Loader', # 1.4 + #'django.template.loaders.filesystem.load_template_source', + #'django.template.loaders.app_directories.load_template_source', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +# 'debug_toolbar.middleware.DebugToolbarMiddleware', +) + +ROOT_URLCONF = 'gramps.webapp.urls' +STATIC_URL = '/static/' # 1.4 + +TEMPLATE_DIRS = ( + # Use absolute paths, not relative paths. + os.path.join(DATA_DIR, "templates"), +) + +TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", # 1.4 + "django.contrib.messages.context_processors.messages", # 1.4 +# "django.core.context_processors.auth", +# "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "gramps.webapp.grampsdb.views.context_processor", + "gramps.webapp.context.messages", +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.staticfiles', + 'django.contrib.messages', # 1.4 + 'django.contrib.sites', + 'django.contrib.admin', + 'gramps.webapp.grampsdb', +# 'django_extensions', +# 'debug_toolbar', +) + +DEBUG_TOOLBAR_PANELS = ( + 'debug_toolbar.panels.version.VersionDebugPanel', + 'debug_toolbar.panels.timer.TimerDebugPanel', + 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', + 'debug_toolbar.panels.headers.HeaderDebugPanel', + 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', + 'debug_toolbar.panels.template.TemplateDebugPanel', + 'debug_toolbar.panels.sql.SQLDebugPanel', + 'debug_toolbar.panels.signals.SignalDebugPanel', + 'debug_toolbar.panels.logger.LoggingPanel', + ) + +def custom_show_toolbar(request): + return True # Always show toolbar, for example purposes only. + +DEBUG_TOOLBAR_CONFIG = { + 'INTERCEPT_REDIRECTS': False, +# 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, +# 'EXTRA_SIGNALS': ['myproject.signals.MySignal'], + 'HIDE_DJANGO_SQL': False, + } + +AUTH_PROFILE_MODULE = "grampsdb.Profile" + +# Had to add these to use settings.configure(): +DATABASE_OPTIONS = '' +URL_VALIDATOR_USER_AGENT = '' +DEFAULT_INDEX_TABLESPACE = '' +DEFAULT_TABLESPACE = '' +CACHE_BACKEND = 'locmem://' +TRANSACTIONS_MANAGED = False +LOCALE_PATHS = tuple() + +# Changes for Django 1.3: +USE_L10N = True +FORMAT_MODULE_PATH = "" +## End Changes for Django 1.3 + +# Changes for Django 1.4: +USE_TZ = False +## End Changes for Django 1.4 + +# Changes for Django 1.5: +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + } + } +DEFAULT_CHARSET = "utf-8" +## End Changes for Django 1.5 + +## Changes for Django 1.5.4: +LOGGING_CONFIG = None +AUTH_USER_MODEL = 'auth.User' +## End Changes for Django 1.5.4 + +LOGIN_URL = "/login/" +LOGOUT_URL = "/logout" +LOGIN_REDIRECT_URL = "/" + +## Changes for Django 1.6: +LOGGING = None + +## Changes for Django 1.7.1: +ABSOLUTE_URL_OVERRIDES = {} diff --git a/gramps/plugins/database/django_support/defaults/sqlite.db b/gramps/plugins/database/django_support/defaults/sqlite.db new file mode 100644 index 0000000000000000000000000000000000000000..a9ac911f065f752dfb25be75e2916ff5df508451 GIT binary patch literal 293888 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®`N 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 N%rWbcAX@iQ{{@Gr__6>1 literal 0 HcmV?d00001 diff --git a/gramps/plugins/database/djangodb.py b/gramps/plugins/database/djangodb.py index 56f40f5ac..e380c9601 100644 --- a/gramps/plugins/database/djangodb.py +++ b/gramps/plugins/database/djangodb.py @@ -30,6 +30,9 @@ import re import base64 import pickle import os +import logging +import shutil +from django.db import transaction #------------------------------------------------------------------------ # @@ -53,13 +56,13 @@ from gramps.gen.db import (PERSON_KEY, REPOSITORY_KEY, NOTE_KEY) from gramps.gen.utils.id import create_id -from django.db import transaction +from gramps.gen.db.dbconst import * ## add this directory to sys path, so we can find django_support later: -import sys -import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) +_LOG = logging.getLogger(DBLOGNAME) + class Environment(object): """ Implements the Environment API. @@ -2041,3 +2044,17 @@ class DbDjango(DbWriteBase, DbReadBase, UpdateCallback, Callback): def restore(self): pass + + def write_version(self, directory): + """Write files for a newly created DB.""" + versionpath = os.path.join(directory, str(DBBACKEND)) + _LOG.debug("Write database backend file to 'djangodb'") + with open(versionpath, "w") as version_file: + version_file.write("djangodb") + # Write default_settings, sqlite.db + defaults = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "django_support", "defaults") + _LOG.debug("Copy defaults from: " + defaults) + for filename in os.listdir(defaults): + fullpath = os.path.abspath(os.path.join(defaults, filename)) + shutil.copy2(fullpath, directory)