diff --git a/.travis.yml b/.travis.yml index f0bb03e56..10c0863a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,9 +21,6 @@ install: # $TRAVIS_BUILD_DIR is set to the location of the cloned repository: # for example: /home/travis/build/gramps-project/gramps - python3 setup.py build - - cd gramps/webapp - - make create - - cd ../.. #before_script: # - sudo Xvfb :99 -ac & @@ -31,4 +28,4 @@ install: script: - mkdir -p ~/.gramps/grampsdb/ - - DJANGO_SETTINGS_MODULE=gramps.webapp.default_settings nosetests3 --exclude=TestcaseGenerator --exclude=vcard --exclude=merge_ref_test gramps + - nosetests3 --exclude=TestcaseGenerator --exclude=vcard --exclude=merge_ref_test gramps diff --git a/MANIFEST.in b/MANIFEST.in index 5beaa91d6..f63399b3a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -29,4 +29,3 @@ global-exclude *.pyo # Remove directories which should not be included in the distribution prune gramps/guiQML -#prune gramps/webapp diff --git a/gramps/webapp/Makefile b/gramps/webapp/Makefile deleted file mode 100644 index 96601c80e..000000000 --- a/gramps/webapp/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -# Initialize GRAMPS Django site - -PYTHON=GRAMPS_RESOURCES=../.. PYTHONPATH=../.. python3 - -update: grampsdb/fixtures/initial_data.json - $(PYTHON) manage.py syncdb --noinput - $(PYTHON) manage.py createsuperuser --username=admin --email=bugs@gramps-project.org - $(PYTHON) manage.py createsuperuser --username=admin1 --email=bugs@gramps-project.org - -create: grampsdb/fixtures/initial_data.json - $(PYTHON) manage.py syncdb --noinput - -grampsdb/fixtures/initial_data.json: init.py - mkdir -p grampsdb/fixtures - $(PYTHON) init.py > grampsdb/fixtures/initial_data.json - -init_gramps: - $(PYTHON) init_gramps.py # clear primary and secondary tables - -run: - $(PYTHON) manage.py runserver - -sql: - $(PYTHON) manage.py sqlall > gramps-sql.sql - -dump: - echo ".dump" | sqlite3 sqlite.db > gramps-data.sql - -load: - sqlite3 sqlite.db < gramps-data.sql - -superusers: - $(PYTHON) manage.py createsuperuser --username=admin --email=bugs@gramps-project.org - $(PYTHON) manage.py createsuperuser --username=admin1 --email=bugs@gramps-project.org - -backup: - $(PYTHON) manage.py dumpdata > backup.json - -restore: empty - $(PYTHON) manage.py loaddata backup.json - -initial_data: - $(PYTHON) manage.py loaddata grampsdb/fixtures/initial_data.json - -docs: - mkdir -p docs - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note -o docs/primary-tables.png - $(PYTHON) graph_models grampsdb -i Note -o docs/note-table.png - $(PYTHON) graph_models grampsdb -i Media -o docs/media-table.png - $(PYTHON) graph_models grampsdb -i Place -o docs/place-table.png - $(PYTHON) graph_models grampsdb -i Repository -o docs/repository-table.png - $(PYTHON) graph_models grampsdb -i Event -o docs/event-table.png - $(PYTHON) graph_models grampsdb -i Source -o docs/source-table.png - $(PYTHON) graph_models grampsdb -i Family -o docs/family-table.png - $(PYTHON) graph_models grampsdb -i Person -o docs/person-table.png - $(PYTHON) graph_models grampsdb -o docs/all-tables.png - $(PYTHON) graph_models grampsdb -i Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/secondary-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/prim-sec-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/prim-sec-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url,NoteRef,SourceRef,EventRef,RepositoryRef,PersonRef,ChildRef,MediaRef -o docs/prim-sec-ref-tables.png - -make-empty: - echo ".dump" | sqlite3 sqlite.db > empty.sql - -empty: - rm -f sqlite.db - sqlite3 sqlite.db < empty.sql - -example: - rm -f sqlite.db - sqlite3 sqlite.db < example.sql - -clean: - rm -f sqlite.db - rm -f *~ *.pyc *.pyo - rm -f grampsdb/fixtures/initial_data.json diff --git a/gramps/webapp/README.md b/gramps/webapp/README.md deleted file mode 100644 index 7ce3c9f9f..000000000 --- a/gramps/webapp/README.md +++ /dev/null @@ -1,114 +0,0 @@ -**UNSTABLE** package for testing. - -This webapp, is a web-based application that runs in your browser, and requires a server. - -Many Gramps users would like to collaborate or share their genealogy data on the web. -The main focus of this Gramps-based webapp is collaboration, allow users to easily move their genealogy data to the web to be seen, and edited with proper login and permissions, in a live, collaborative environment. - -A prototype is on-line at http://gramps-connect.org/ - -**Requires** -Django (version 1.7 supported until October 2015) (version 1.8 LTS supported until April 2018) ( https://www.djangoproject.com/ ) - -**Webapp** - -- 'gramps-webapp' packages are in progress... - -For **Testing only**. - -See https://www.gramps-project.org/wiki/index.php?title=Gramps-Connect for more details. - -**Some commands** - -$ cd /usr/lib/python3.4/dist-packages/gramps/webapp -$ python manage.py help -$ sudo make - -# Initialize Gramps Django site - -PYTHON=GRAMPS_RESOURCES=../.. PYTHONPATH=../.. python3.4 - - -- update: grampsdb/fixtures/initial_data.json - $(PYTHON) manage.py syncdb --noinput - $(PYTHON) manage.py createsuperuser --username=admin --email=bugs@gramps-project.org - $(PYTHON) manage.py createsuperuser --username=admin1 --email=bugs@gramps-project.org - - -- grampsdb/fixtures/initial_data.json: init.py - mkdir -p grampsdb/fixtures - $(PYTHON) init.py > grampsdb/fixtures/initial_data.json - - -- init_gramps: - $(PYTHON) init_gramps.py # clear primary and secondary tables - - -- run: - $(PYTHON) manage.py runserver - - -- sql: - $(PYTHON) manage.py sqlall > gramps-sql.sql - -- dump: - echo ".dump" | sqlite3 sqlite.db > gramps-data.sql - - -- load: - sqlite3 sqlite.db < gramps-data.sql - - -- superusers: - $(PYTHON) manage.py createsuperuser --username=admin --email=bugs@gramps-project.org - $(PYTHON) manage.py createsuperuser --username=admin1 --email=bugs@gramps-project.org - - -- backup: - $(PYTHON) manage.py dumpdata > backup.json - - -- restore: empty - $(PYTHON) manage.py loaddata backup.json - - -- initial_data: - $(PYTHON) manage.py loaddata grampsdb/fixtures/initial_data.json - - -- docs: - mkdir -p docs - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note -o docs/primary-tables.png - $(PYTHON) graph_models grampsdb -i Note -o docs/note-table.png - $(PYTHON) graph_models grampsdb -i Media -o docs/media-table.png - $(PYTHON) graph_models grampsdb -i Place -o docs/place-table.png - $(PYTHON) graph_models grampsdb -i Repository -o docs/repository-table.png - $(PYTHON) graph_models grampsdb -i Event -o docs/event-table.png - $(PYTHON) graph_models grampsdb -i Source -o docs/source-table.png - $(PYTHON) graph_models grampsdb -i Family -o docs/family-table.png - $(PYTHON) graph_models grampsdb -i Person -o docs/person-table.png - $(PYTHON) graph_models grampsdb -o docs/all-tables.png - $(PYTHON) graph_models grampsdb -i Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/secondary-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/prim-sec-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/prim-sec-tables.png - $(PYTHON) graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note,Attribute,Datamap,Name,Lds,Tag,Address,Location,Url,NoteRef,SourceRef,EventRef,RepositoryRef,PersonRef,ChildRef,MediaRef -o docs/prim-sec-ref-tables.png - - -- make-empty: - echo ".dump" | sqlite3 sqlite.db > empty.sql - - -- empty: - rm -f sqlite.db - sqlite3 sqlite.db < empty.sql - - -- example: - rm -f sqlite.db - sqlite3 sqlite.db < example.sql - - -- clean: - rm -f sqlite.db - rm -f *~ *.pyc *.pyo - rm -f grampsdb/fixtures/initial_data.json diff --git a/gramps/webapp/__init__.py b/gramps/webapp/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/gramps/webapp/client.py b/gramps/webapp/client.py deleted file mode 100644 index 35850965e..000000000 --- a/gramps/webapp/client.py +++ /dev/null @@ -1,47 +0,0 @@ -import urllib2 -import urllib - -import logging -LOG = logging.getLogger(".web") - -PATH_TO_LOGIN = "login/" - -def login(base_address, username=None, password=None): - # fetch the login page in order to get the csrf token - cookieHandler = urllib2.HTTPCookieProcessor() - opener = urllib2.build_opener(urllib2.HTTPSHandler(), cookieHandler) - urllib2.install_opener(opener) - login_url = base_address + PATH_TO_LOGIN - LOG.info("login_url: " + login_url) - login_page = opener.open(login_url) - # attempt to get the csrf token from the cookie jar - csrf_cookie = None - for cookie in cookieHandler.cookiejar: - if cookie.name == 'csrftoken': - csrf_cookie = cookie - break - if not cookie: - raise IOError("No csrf cookie found") - LOG.info( "found csrf cookie: " + str(csrf_cookie)) - LOG.info( "csrf_token = %s" % csrf_cookie.value) - # login using the usr, pwd, and csrf token - login_data = urllib.urlencode(dict( - username=username, password=password, - csrfmiddlewaretoken=csrf_cookie.value)) - LOG.info("login_data: %s" % login_data) - LOG.info("login_url: %s" % login_url) - req = urllib2.Request(login_url, login_data) - req.add_header('Referer', login_url) - response = urllib2.urlopen(req) - # <--- 403: FORBIDDEN here - LOG.info('response url:\n' + str(response.geturl()) + '\n') - LOG.info('response info:\n' + str(response.info()) + '\n') - # should redirect to the welcome page here, if back at log in - refused - url = response.geturl() - LOG.info("url:", url) - LOG.info("login_url:", login_url) - if url == login_url: - return None - LOG.info('\t%s is logged in' % username) - # save the cookies/opener for further actions - return opener diff --git a/gramps/webapp/connection.py b/gramps/webapp/connection.py deleted file mode 100644 index c93427f50..000000000 --- a/gramps/webapp/connection.py +++ /dev/null @@ -1,49 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2012 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -from urllib.request import (Request, urlopen, HTTPCookieProcessor, - build_opener, install_opener) -from urllib.parse import urlencode - -class Connection(object): - """ - >>> conn = Connection() - >>> response = conn.login("http://blankfamily.us/login/", "username", "password") - """ - def login(self, login_url, username, password): - cookies = HTTPCookieProcessor() - opener = build_opener(cookies) - install_opener(opener) - opener.open(login_url) - try: - self.token = [x.value for x in cookies.cookiejar if x.name == 'csrftoken'][0] - except IndexError: - return Exception("no csrftoken") - params = dict(username=username, - password=password, - next="/", - csrfmiddlewaretoken=self.token, - ) - login_data = urlencode(params) - request = Request(login_url, login_data) - response = urlopen(request) - if response.geturl() == login_url: - raise Exception("Invalid password") - return response - diff --git a/gramps/webapp/context.py b/gramps/webapp/context.py deleted file mode 100644 index d706943a0..000000000 --- a/gramps/webapp/context.py +++ /dev/null @@ -1,29 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2012 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -def messages(request): - messages = {} - if 'message' in request.session: - message_type = request.session.get('message_type', 'error') - messages = {'message': request.session['message'], - 'message_type': message_type} - del request.session['message'] - if 'message_type' in request.session: - del request.session['message_type'] - return messages diff --git a/gramps/webapp/default_settings.py b/gramps/webapp/default_settings.py deleted file mode 100644 index 6733c3ab8..000000000 --- a/gramps/webapp/default_settings.py +++ /dev/null @@ -1,175 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Django settings for gramps project. """ - -# Need to be able to import Gramps files from here. - -import os -if not 'GRAMPS_RESOURCES' in os.environ: - os.environ['GRAMPS_RESOURCES'] = os.path.dirname(os.path.abspath("../..")) - -from gramps.gen.const import DATA_DIR, WEB_DIR - -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 = {} -TEST_RUNNER = 'django.test.runner.DiscoverRunner' diff --git a/gramps/webapp/djangodb.py b/gramps/webapp/djangodb.py deleted file mode 100644 index c85de26ce..000000000 --- a/gramps/webapp/djangodb.py +++ /dev/null @@ -1,1149 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Implements a Db interface """ - -#------------------------------------------------------------------------ -# -# Gramps Modules -# -#------------------------------------------------------------------------ -import pickle -import sys -import os - -## add this directory to sys path, so we can find django_support later: -sys.path.append(os.path.dirname(os.path.abspath(__file__))) - -#------------------------------------------------------------------------ -# -# Gramps Modules -# -#------------------------------------------------------------------------ -from gramps.gen.db.generic import * -from gramps.gen.const import GRAMPS_LOCALE as glocale -_ = glocale.translation.gettext - -class DbDjango(DbGeneric): - - 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)) - if os.path.isfile(fullpath): - shutil.copy2(fullpath, directory) - # force load, to get all modules loaded because of reset issue - self.load(directory) - - def initialize_backend(self, directory): - pass - - def close_backend(self): - pass - - def transaction_commit(self, txn): - for (obj_type, trans_type) in txn.keys(): - if trans_type in [TXNUPD, TXNADD]: - for (handle, new_data) in txn[(obj_type, trans_type)]: - if obj_type == PERSON_KEY: - self.commit_person_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == FAMILY_KEY: - self.commit_family_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == EVENT_KEY: - self.commit_event_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == PLACE_KEY: - self.commit_place_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == REPOSITORY_KEY: - self.commit_repository_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == CITATION_KEY: - self.commit_citation_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == SOURCE_KEY: - self.commit_source_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == NOTE_KEY: - self.commit_note_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == MEDIA_KEY: - self.commit_media_object_detail(handle, new_data, trans_type, txn.batch) - elif obj_type == TAG_KEY: - self.commit_tag_detail(handle, new_data, trans_type, txn.batch) - if txn.batch and self.has_changed: - self.rebuild_secondary(None) - - def transaction_abort(self, txn): - pass - - def get_metadata(self, setting, default=[]): - metadata = self.dji.Metadata.filter(setting=setting) - if metadata.count() > 0: - return pickle.loads(metadata[0].value) - elif default == []: - return [] - else: - return default - - def set_metadata(self, setting, value): - from gramps.webapp.grampsdb.models import Metadata - metadata = self.dji.Metadata.filter(setting=setting) - if metadata.count() > 0: - metadata = metadata[0] - metadata.value = pickle.dumps(value) - else: - metadata = Metadata(setting=setting, value=pickle.dumps(value)) - metadata.save() - - def get_name_group_keys(self): - rows = self.dji.NameGroup.all().order_by('name') - return [row.name for row in rows] - - def get_name_group_mapping(self, key): - rows = self.dji.NameGroup.filter(name=key) - if rows: - return row[0].name - else: - return key - - def get_person_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Person.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Person.all()] - - def get_family_handles(self): - return [item.handle for item in self.dji.Family.all()] - - def get_event_handles(self): - return [item.handle for item in self.dji.Event.all()] - - def get_citation_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Citation.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Citation.all()] - - def get_source_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Source.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Source.all()] - - def get_place_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Place.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Place.all()] - - def get_repository_handles(self): - return [item.handle for item in self.dji.Repository.all()] - - def get_media_object_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Media.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Media.all()] - - def get_note_handles(self): - return [item.handle for item in self.dji.Note.all()] - - def get_tag_handles(self, sort_handles=False): - if sort_handles: - return [item.handle for item in self.dji.Tag.all().order_by("handle")] - else: - return [item.handle for item in self.dji.Tag.all()] - - def get_tag_from_name(self, name): - try: - tag = self.dji.Tag.filter(name=name) - return self._make_tag(tag[0]) - except: - return None - - def get_number_of_people(self): - return self.dji.Person.count() - - def get_number_of_events(self): - return self.dji.Event.count() - - def get_number_of_places(self): - return self.dji.Place.count() - - def get_number_of_tags(self): - return self.dji.Tag.count() - - def get_number_of_families(self): - return self.dji.Family.count() - - def get_number_of_notes(self): - return self.dji.Note.count() - - def get_number_of_citations(self): - return self.dji.Citation.count() - - def get_number_of_sources(self): - return self.dji.Source.count() - - def get_number_of_media_objects(self): - return self.dji.Media.count() - - def get_number_of_repositories(self): - return self.dji.Repository.count() - - def has_name_group_key(self, key): - return len(self.dji.NameGroup.filter(name=key)) > 0 - - def set_name_group_mapping(self, name, grouping): - from gramps.webapp.grampsdb.models import NameGroup - if self.has_name_group_key(name): - namegroup = self.dji.NameGroup.get(name=name) - else: - namegroup = NameGroup(name=name) - namegroup.grouping = grouping - namegroup.save() - - def commit_person(self, person, trans, change_time=None): - raw = person.serialize() - items = self.dji.Person.filter(handle=person.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_person_data(person.handle) - # delete and re-add - items[0].delete() - self.dji.add_person(raw) - if count > 0: - trans.add(PERSON_KEY, TXNUPD, person.handle, old, raw) - else: - trans.add(PERSON_KEY, TXNADD, person.handle, old, raw) - # Contiued in transaction_commit... - - def commit_person_detail(self, handle, new_data, trans_type, batch): - old_obj = self.get_person_from_handle(handle) - self.dji.add_person_detail(new_data) - obj = self.get_person_from_handle(handle) - if trans_type == TXNUPD: - if (old_obj.gender != obj.gender or - old_obj.primary_name.first_name != - obj.primary_name.first_name): - self.genderStats.uncount_person(old_obj) - self.genderStats.count_person(obj) - elif trans_type == TXNADD: - self.genderStats.count_person(person) - person = obj - # Other misc update tasks: - self.individual_attributes.update( - [str(attr.type) for attr in person.attribute_list - if attr.type.is_custom() and str(attr.type)]) - - self.event_role_names.update([str(eref.role) - for eref in person.event_ref_list - if eref.role.is_custom()]) - - self.name_types.update([str(name.type) - for name in ([person.primary_name] - + person.alternate_names) - if name.type.is_custom()]) - all_surn = [] # new list we will use for storage - all_surn += person.primary_name.get_surname_list() - for asurname in person.alternate_names: - all_surn += asurname.get_surname_list() - self.origin_types.update([str(surn.origintype) for surn in all_surn - if surn.origintype.is_custom()]) - all_surn = None - self.url_types.update([str(url.type) for url in person.urls - if url.type.is_custom()]) - attr_list = [] - for mref in person.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("person-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("person-add", ([handle],)) - self.has_changed = True - - def commit_family(self, family, trans, change_time=None): - raw = family.serialize() - items = self.dji.Family.filter(handle=family.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_family_data(family.handle) - # delete and re-add - items[0].delete() - self.dji.add_family(family.serialize()) - if count > 0: - trans.add(FAMILY_KEY, TXNUPD, family.handle, old, raw) - else: - trans.add(FAMILY_KEY, TXNADD, family.handle, old, raw) - # Contiued in transaction_commit... - - def commit_family_detail(self, handle, new_data, trans_type, batch): - self.dji.add_family_detail(new_data) - obj = self.get_family_from_handle(handle) - family = obj - # Misc updates: - self.family_attributes.update( - [str(attr.type) for attr in family.attribute_list - if attr.type.is_custom() and str(attr.type)]) - - rel_list = [] - for ref in family.child_ref_list: - if ref.frel.is_custom(): - rel_list.append(str(ref.frel)) - if ref.mrel.is_custom(): - rel_list.append(str(ref.mrel)) - self.child_ref_types.update(rel_list) - - self.event_role_names.update( - [str(eref.role) for eref in family.event_ref_list - if eref.role.is_custom()]) - - if family.type.is_custom(): - self.family_rel_types.add(str(family.type)) - - attr_list = [] - for mref in family.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("family-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("family-add", ([handle],)) - self.has_changed = True - - def commit_citation(self, citation, trans, change_time=None): - raw = citation.serialize() - items = self.dji.Citation.filter(handle=citation.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_citation_data(citation.handle) - # delete and re-add - items[0].delete() - self.dji.add_citation(citation.serialize()) - if count > 0: - trans.add(CITATION_KEY, TXNUPD, citation.handle, old, raw) - else: - trans.add(CITATION_KEY, TXNADD, citation.handle, old, raw) - # Contiued in transaction_commit... - - def commit_citation_detail(self, handle, new_data, trans_type, batch): - self.dji.add_citation_detail(new_data) - obj = self.get_citation_from_handle(handle) - citation = obj - # Misc updates: - attr_list = [] - for mref in citation.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - - self.source_attributes.update( - [str(attr.type) for attr in citation.attribute_list - if attr.type.is_custom() and str(attr.type)]) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("citation-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("citation-add", ([handle],)) - self.has_changed = True - - def commit_source(self, source, trans, change_time=None): - raw = source.serialize() - items = self.dji.Source.filter(handle=source.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_source_data(source.handle) - # delete and re-add - items[0].delete() - self.dji.add_source(source.serialize()) - if count > 0: - trans.add(SOURCE_KEY, TXNUPD, source.handle, old, raw) - else: - trans.add(SOURCE_KEY, TXNADD, source.handle, old, raw) - # Contiued in transaction_commit... - - def commit_source_detail(self, handle, new_data, trans_type, batch): - self.dji.add_source_detail(new_data) - obj = self.get_source_from_handle(handle) - source = obj - # Misc updates: - self.source_media_types.update( - [str(ref.media_type) for ref in source.reporef_list - if ref.media_type.is_custom()]) - - attr_list = [] - for mref in source.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - self.source_attributes.update( - [str(attr.type) for attr in source.attribute_list - if attr.type.is_custom() and str(attr.type)]) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("source-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("source-add", ([handle],)) - self.has_changed = True - - def commit_repository(self, repository, trans, change_time=None): - raw = repository.serialize() - items = self.dji.Repository.filter(handle=repository.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_repository_data(repository.handle) - # delete and re-add - items[0].delete() - self.dji.add_repository(repository.serialize()) - if count > 0: - trans.add(REPOSITORY_KEY, TXNUPD, repository.handle, old, raw) - else: - trans.add(REPOSITORY_KEY, TXNADD, repository.handle, old, raw) - # Contiued in transaction_commit... - - def commit_repository_detail(self, handle, new_data, trans_type, batch): - self.dji.add_repository_detail(new_data) - obj = self.get_repository_from_handle(handle) - repository = obj - # Misc updates: - if repository.type.is_custom(): - self.repository_types.add(str(repository.type)) - - self.url_types.update([str(url.type) for url in repository.urls - if url.type.is_custom()]) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("repository-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("repository-add", ([handle],)) - self.has_changed = True - - def commit_note(self, note, trans, change_time=None): - raw = note.serialize() - items = self.dji.Note.filter(handle=note.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_note_data(note.handle) - # delete and re-add - items[0].delete() - self.dji.add_note(note.serialize()) - if count > 0: - trans.add(NOTE_KEY, TXNUPD, note.handle, old, raw) - else: - trans.add(NOTE_KEY, TXNADD, note.handle, old, raw) - # Contiued in transaction_commit... - - def commit_note_detail(self, handle, new_data, trans_type, batch): - self.dji.add_note_detail(new_data) - obj = self.get_note_from_handle(handle) - note = obj - # Misc updates: - if note.type.is_custom(): - self.note_types.add(str(note.type)) - # Emit after added: - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("note-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("note-add", ([handle],)) - self.has_changed = True - - def commit_place(self, place, trans, change_time=None): - raw = place.serialize() - items = self.dji.Place.filter(handle=place.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_place_data(place.handle) - # delete and re-add - items[0].delete() - self.dji.add_place(place.serialize()) - if count > 0: - trans.add(PLACE_KEY, TXNUPD, place.handle, old, raw) - else: - trans.add(PLACE_KEY, TXNADD, place.handle, old, raw) - # Contiued in transaction_commit... - - def commit_place_detail(self, handle, new_data, trans_type, batch): - self.dji.add_place_detail(new_data) - obj = self.get_place_from_handle(handle) - place = obj - # Misc updates: - if place.get_type().is_custom(): - self.place_types.add(str(place.get_type())) - - self.url_types.update([str(url.type) for url in place.urls - if url.type.is_custom()]) - - attr_list = [] - for mref in place.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("place-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("place-add", ([handle],)) - self.has_changed = True - - def commit_event(self, event, trans, change_time=None): - raw = event.serialize() - items = self.dji.Event.filter(handle=event.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_event_data(event.handle) - # delete and re-add - items[0].delete() - self.dji.add_event(event.serialize()) - if count > 0: - trans.add(EVENT_KEY, TXNUPD, event.handle, old, raw) - else: - trans.add(EVENT_KEY, TXNADD, event.handle, old, raw) - # Contiued in transaction_commit... - - def commit_event_detail(self, handle, new_data, trans_type, batch): - self.dji.add_event_detail(new_data) - obj = self.get_event_from_handle(handle) - event = obj - # Misc updates: - self.event_attributes.update( - [str(attr.type) for attr in event.attribute_list - if attr.type.is_custom() and str(attr.type)]) - if event.type.is_custom(): - self.event_names.add(str(event.type)) - attr_list = [] - for mref in event.media_list: - attr_list += [str(attr.type) for attr in mref.attribute_list - if attr.type.is_custom() and str(attr.type)] - self.media_attributes.update(attr_list) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("event-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("event-add", ([handle],)) - self.has_changed = True - - def commit_tag(self, tag, trans, change_time=None): - raw = tag.serialize() - items = self.dji.Tag.filter(handle=tag.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_tag_data(tag.handle) - # delete and re-add - items[0].delete() - self.dji.add_tag(tag.serialize()) - if count > 0: - trans.add(TAG_KEY, TXNUPD, tag.handle, old, raw) - else: - trans.add(TAG_KEY, TXNADD, tag.handle, old, raw) - # Contiued in transaction_commit... - - def commit_tag_detail(self, handle, new_data, trans_type, batch): - self.dji.add_tag_detail(new_data) - obj = self.get_tag_from_handle(handle) - tag = obj - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("tag-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("tag-add", ([handle],)) - self.has_changed = True - - def commit_media_object(self, media, trans, change_time=None): - """ - Commit the specified MediaObject to the database, storing the changes - as part of the transaction. - """ - raw = media.serialize() - items = self.dji.Media.filter(handle=media.handle) - count = items.count() - old = None - if count > 0: - old = self._get_raw_media_data(media.handle) - # delete and re-add - items[0].delete() - self.dji.add_media(media.serialize()) - if count > 0: - trans.add(MEDIA_KEY, TXNUPD, media.handle, old, raw) - else: - trans.add(MEDIA_KEY, TXNADD, media.handle, old, raw) - # Contiued in transaction_commit... - - def commit_media_object_detail(self, handle, new_data, trans_type, batch): - self.dji.add_media_detail(new_data) - obj = self.get_object_from_handle(handle) - media = obj - # Misc updates: - self.media_attributes.update( - [str(attr.type) for attr in media.attribute_list - if attr.type.is_custom() and str(attr.type)]) - if not batch: - self.update_backlinks(obj) - if trans_type == TXNUPD: - self.emit("media-update", ([handle],)) - elif trans_type == TXNADD: - self.emit("media-add", ([handle],)) - self.has_changed = True - - def find_backlink_handles(self, handle, include_classes=None): - """ - Find all objects that hold a reference to the object handle. - - Returns an interator over a list of (class_name, handle) tuples. - - :param handle: handle of the object to search for. - :type handle: database handle - :param include_classes: list of class names to include in the results. - Default: None means include all classes. - :type include_classes: list of class names - - Note that this is a generator function, it returns a iterator for - use in loops. If you want a list of the results use:: - - result_list = list(find_backlink_handles(handle)) - """ - rows = self.dji.Reference.filter(ref_handle=handle) - for row in rows: - if (include_classes is None) or (row.obj_class in include_classes): - yield (row.obj_class, row.obj_handle) - - def update_backlinks(self, obj): - from gramps.webapp.grampsdb.models import Reference - # First, delete the current references: - self.dji.Reference.filter(obj_handle=obj.handle).delete() - # Now, add the current ones: - references = set(obj.get_referenced_handles_recursively()) - for (ref_class_name, ref_handle) in references: - reference = Reference(obj_handle=obj.handle, - obj_class=obj.__class__.__name__, - ref_handle=ref_handle, - ref_class=ref_class_name) - reference.save() - - # Removals: - def remove_person(self, handle, transaction): - self.dji.Person.filter(handle=handle)[0].delete() - self.emit("person-delete", ([handle],)) - - def _do_remove(self, handle, transaction, data_map, data_id_map, key): - key2table = { - PERSON_KEY: "person", - FAMILY_KEY: "family", - SOURCE_KEY: "source", - CITATION_KEY: "citation", - EVENT_KEY: "event", - MEDIA_KEY: "media", - PLACE_KEY: "place", - REPOSITORY_KEY: "repository", - NOTE_KEY: "note", - TAG_KEY: "tag", - } - table = getattr(self.dji, key2table[key].title()) - table.filter(handle=handle)[0].delete() - self.emit("%s-delete" % key2table[key], ([handle],)) - - def find_initial_person(self): - handle = self.get_default_handle() - person = None - if handle: - person = self.get_person_from_handle(handle) - if person: - return person - if len(self.dji.Person.all()) > 0: - person = self.dji.Person.all()[0] - return self.get_person_from_handle(person.handle) - - def iter_person_handles(self): - return (person.handle for person in self.dji.Person.all()) - - def iter_family_handles(self): - return (family.handle for family in self.dji.Family.all()) - - def iter_citation_handles(self): - return (citation.handle for citation in self.dji.Citation.all()) - - def iter_event_handles(self): - return (event.handle for event in self.dji.Event.all()) - - def iter_media_object_handles(self): - return (media.handle for media in self.dji.Media.all()) - - def iter_note_handles(self): - return (note.handle for note in self.dji.Note.all()) - - def iter_place_handles(self): - return (place.handle for place in self.dji.Place.all()) - - def iter_repository_handles(self): - return (repository.handle for repository in self.dji.Repository.all()) - - def iter_source_handles(self): - return (source.handle for source in self.dji.Source.all()) - - def iter_tag_handles(self): - return (tag.handle for tag in self.dji.Tag.all()) - - def reindex_reference_map(self, callback): - from gramps.webapp.grampsdb.models import Reference - callback(4) - self.dji.Reference.all().delete() - primary_table = ( - (self.get_person_cursor, Person), - (self.get_family_cursor, Family), - (self.get_event_cursor, Event), - (self.get_place_cursor, Place), - (self.get_source_cursor, Source), - (self.get_citation_cursor, Citation), - (self.get_media_cursor, MediaObject), - (self.get_repository_cursor, Repository), - (self.get_note_cursor, Note), - (self.get_tag_cursor, Tag), - ) - # Now we use the functions and classes defined above - # to loop through each of the primary object tables. - for cursor_func, class_func in primary_table: - logging.info("Rebuilding %s reference map" % - class_func.__name__) - with cursor_func() as cursor: - for found_handle, val in cursor: - obj = class_func.create(val) - references = set(obj.get_referenced_handles_recursively()) - # handle addition of new references - for (ref_class_name, ref_handle) in references: - reference = Reference(obj_handle=obj.handle, - obj_class=obj.__class__.__name__, - ref_handle=ref_handle, - ref_class=ref_class_name) - reference.save() - callback(5) - - def rebuild_secondary(self, update): - gstats = self.rebuild_gender_stats() - self.genderStats = GenderStats(gstats) - - def has_handle_for_person(self, key): - return self.dji.Person.filter(handle=key).count() > 0 - - def has_handle_for_family(self, key): - return self.dji.Family.filter(handle=key).count() > 0 - - def has_handle_for_source(self, key): - return self.dji.Source.filter(handle=key).count() > 0 - - def has_handle_for_citation(self, key): - return self.dji.Citation.filter(handle=key).count() > 0 - - def has_handle_for_event(self, key): - return self.dji.Event.filter(handle=key).count() > 0 - - def has_handle_for_media(self, key): - return self.dji.Media.filter(handle=key).count() > 0 - - def has_handle_for_place(self, key): - return self.dji.Place.filter(handle=key).count() > 0 - - def has_handle_for_repository(self, key): - return self.dji.Repository.filter(handle=key).count() > 0 - - def has_handle_for_note(self, key): - return self.dji.Note.filter(handle=key).count() > 0 - - def has_handle_for_tag(self, key): - return self.dji.Tag.filter(handle=key).count() > 0 - - def has_gramps_id_for_person(self, key): - return self.dji.Person.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_family(self, key): - return self.dji.Family.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_source(self, key): - return self.dji.Source.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_citation(self, key): - return self.dji.Citation.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_event(self, key): - return self.dji.Event.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_media(self, key): - return self.dji.Media.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_place(self, key): - return self.dji.Place.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_repository(self, key): - return self.dji.Repository.filter(gramps_id=key).count() > 0 - - def has_gramps_id_for_note(self, key): - return self.dji.Note.filter(gramps_id=key).count() > 0 - - def get_person_gramps_ids(self): - return [x.gramps_id for x in self.dji.Person.all()] - - def get_family_gramps_ids(self): - return [x.gramps_id for x in self.dji.Family.all()] - - def get_source_gramps_ids(self): - return [x.gramps_id for x in self.dji.Source.all()] - - def get_citation_gramps_ids(self): - return [x.gramps_id for x in self.dji.Citation.all()] - - def get_event_gramps_ids(self): - return [x.gramps_id for x in self.dji.Event.all()] - - def get_media_gramps_ids(self): - return [x.gramps_id for x in self.dji.Media.all()] - - def get_place_gramps_ids(self): - return [x.gramps_id for x in self.dji.Place.all()] - - def get_repository_gramps_ids(self): - return [x.gramps_id for x in self.dji.Repository.all()] - - def get_note_gramps_ids(self): - return [x.gramps_id for x in self.dji.Note.all()] - - def _get_raw_person_data(self, key): - try: - return self.dji.get_person(self.dji.Person.get(handle=key)) - except: - return None - - def _get_raw_person_from_id_data(self, key): - try: - return self.dji.get_person(self.dji.Person.get(gramps_id=key)) - except: - return None - - def _get_raw_family_data(self, key): - try: - return self.dji.get_family(self.dji.Family.get(handle=key)) - except: - return None - - def _get_raw_family_from_id_data(self, key): - try: - return self.dji.get_family(self.dji.Family.get(gramps_id=key)) - except: - return None - - def _get_raw_source_data(self, key): - try: - return self.dji.get_source(self.dji.Source.get(handle=key)) - except: - return None - - def _get_raw_source_from_id_data(self, key): - try: - return self.dji.get_source(self.dji.Source.get(gramps_id=key)) - except: - return None - - def _get_raw_citation_data(self, key): - try: - return self.dji.get_citation(self.dji.Citation.get(handle=key)) - except: - return None - - def _get_raw_citation_from_id_data(self, key): - try: - return self.dji.get_citation(self.dji.Citation.get(gramps_id=key)) - except: - return None - - def _get_raw_event_data(self, key): - try: - return self.dji.get_event(self.dji.Event.get(handle=key)) - except: - return None - - def _get_raw_event_from_id_data(self, key): - try: - return self.dji.get_event(self.dji.Event.get(gramps_id=key)) - except: - return None - - def _get_raw_media_data(self, key): - try: - return self.dji.get_media(self.dji.Media.get(handle=key)) - except: - return None - - def _get_raw_media_from_id_data(self, key): - try: - return self.dji.get_media(self.dji.Media.get(gramps_id=key)) - except: - return None - - def _get_raw_place_data(self, key): - try: - return self.dji.get_place(self.dji.Place.get(handle=key)) - except: - return None - - def _get_raw_place_from_id_data(self, key): - try: - return self.dji.get_place(self.dji.Place.get(gramps_id=key)) - except: - return None - - def _get_raw_repository_data(self, key): - try: - return self.dji.get_repository(self.dji.Repository.get(handle=key)) - except: - return None - - def _get_raw_repository_from_id_data(self, key): - try: - return self.dji.get_repository(self.dji.Repository.get(gramps_id=key)) - except: - return None - - def _get_raw_note_data(self, key): - try: - return self.dji.get_note(self.dji.Note.get(handle=key)) - except: - return None - - def _get_raw_note_from_id_data(self, key): - try: - return self.dji.get_note(self.dji.Note.get(gramps_id=key)) - except: - return None - - def _get_raw_tag_data(self, key): - try: - return self.dji.get_tag(self.dji.Tag.get(handle=key)) - except: - return None - - def rebuild_gender_stats(self): - """ - Returns a dictionary of - {given_name: (male_count, female_count, unknown_count)} - Not called: this is a database-efficient version - """ - UNKNOWN = 2 - MALE = 1 - FEMALE = 0 - self.dji.GenderStats.all().delete() - gstats = {} - for person in self.dji.Person.all(): - for first_name in person.name_set.all(): - for name in first_name.first_name.split(): - if name not in gstats: - gstats[name] = [0, 0, 0] - if person.gender_type.val == MALE: - gstats[name][0] += 1 - elif person.gender_type.val == FEMALE: - gstats[name][1] += 1 - else: - gstats[name][2] += 1 - for key in gstats: - gstats[key] = tuple(gstats[key]) - return gstats - - def save_gender_stats(self, genderStats): - """ - {name: (male_count, female_count, unknown_count), ...} - """ - from gramps.webapp.grampsdb.models import GenderStats - self.dji.GenderStats.all().delete() - gstats = genderStats.stats - for key in gstats: - data = gstats[key] - stat = GenderStats(name=key, - male=data[0], - female=data[1], - unknown=data[2]) - stat.save() - - def get_gender_stats(self): - """ - Returns a dictionary of - {given_name: (male_count, female_count, unknown_count)} - """ - rows = self.dji.GenderStats.values('name', 'male', 'female', 'unknown') - gstats = {} - for dict in rows: - gstats[dict['name']] = (dict['male'], dict['female'], dict['unknown']) - return gstats - - def get_surname_list(self): - return [x['surname'] for x in self.dji.Surname.values('surname').order_by('surname').distinct()] - - def save_surname_list(self): - # Nothing to do - pass - - def build_surname_list(self): - # Nothing to do - pass - - def drop_tables(self): - # Nothing to do - pass - - def load(self, directory, callback=None, mode=None, - force_schema_upgrade=False, - force_bsddb_upgrade=False, - force_bsddb_downgrade=False, - force_python_upgrade=False): - - # Django-specific loads: - from django.conf import settings - - LOG.info("Django loading...") - default_settings = {"__file__": - os.path.join(directory, "default_settings.py")} - settings_file = os.path.join(directory, "default_settings.py") - with open(settings_file) as f: - code = compile(f.read(), settings_file, 'exec') - exec(code, globals(), default_settings) - - class Module(object): - def __init__(self, dictionary): - self.dictionary = dictionary - def __getattr__(self, item): - return self.dictionary[item] - - LOG.info("Django loading defaults from: " + directory) - try: - settings.configure(Module(default_settings)) - except RuntimeError: - LOG.info("Django already configured error! Shouldn't happen!") - # already configured; ignore - - import django - django.setup() - - from gramps.webapp.libdjango import DjangoInterface - - self.dji = DjangoInterface() - super().load(directory, - callback, - mode, - force_schema_upgrade, - force_bsddb_upgrade, - force_bsddb_downgrade, - force_python_upgrade) - - def _make_repository(self, repository): - if self.use_db_cache and repository.cache: - data = repository.from_cache() - else: - data = self.dji.get_repository(repository) - return Repository.create(data) - - def _make_citation(self, citation): - if self.use_db_cache and citation.cache: - data = citation.from_cache() - else: - data = self.dji.get_citation(citation) - return Citation.create(data) - - def _make_source(self, source): - if self.use_db_cache and source.cache: - data = source.from_cache() - else: - data = self.dji.get_source(source) - return Source.create(data) - - def _make_family(self, family): - if self.use_db_cache and family.cache: - data = family.from_cache() - else: - data = self.dji.get_family(family) - return Family.create(data) - - def _make_person(self, person): - if self.use_db_cache and person.cache: - data = person.from_cache() - else: - data = self.dji.get_person(person) - return Person.create(data) - - def _make_event(self, event): - if self.use_db_cache and event.cache: - data = event.from_cache() - else: - data = self.dji.get_event(event) - return Event.create(data) - - def _make_note(self, note): - if self.use_db_cache and note.cache: - data = note.from_cache() - else: - data = self.dji.get_note(note) - return Note.create(data) - - def _make_tag(self, tag): - data = self.dji.get_tag(tag) - return Tag.create(data) - - def _make_place(self, place): - if self.use_db_cache and place.cache: - data = place.from_cache() - else: - data = self.dji.get_place(place) - return Place.create(data) - - def _make_media(self, media): - if self.use_db_cache and media.cache: - data = media.from_cache() - else: - data = self.dji.get_media(media) - return MediaObject.create(data) - - def request_rebuild(self): # override - # caches are ok, but let's compute public's - self.dji.update_publics() - super().request_rebuild() diff --git a/gramps/webapp/grampsdb/__init__.py b/gramps/webapp/grampsdb/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/gramps/webapp/grampsdb/admin.py b/gramps/webapp/grampsdb/admin.py deleted file mode 100644 index cb6f15302..000000000 --- a/gramps/webapp/grampsdb/admin.py +++ /dev/null @@ -1,37 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -# webapp/grampsdb/admin.py - -from gramps.webapp.grampsdb.models import * -from django.contrib import admin - -## FIXME: this no longer works in Django 1.5 -class MyAdmin(admin.ModelAdmin): - def change_view(self, request, object_id, extra_context=None): - result = super(MyAdmin, self).change_view(request, object_id, extra_context) - if '_addanother' not in request.POST and '_continue' not in request.POST: - result['Location'] = "/" - return result - -for type_name in get_tables("all"): - admin.site.register(type_name[1], admin.ModelAdmin) -admin.site.register(Profile, admin.ModelAdmin) - diff --git a/gramps/webapp/grampsdb/forms.py b/gramps/webapp/grampsdb/forms.py deleted file mode 100644 index 3d4784e4c..000000000 --- a/gramps/webapp/grampsdb/forms.py +++ /dev/null @@ -1,306 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -# webapp/grampsdb/forms.py - -# forms.py forms for Django web project - -# Django Modules: -from django import forms -from django.forms.models import inlineformset_factory -from django.forms.models import BaseModelFormSet -from django.forms.widgets import TextInput, HiddenInput - -# Gramps Modules: -from gramps.webapp.grampsdb.models import * -from gramps.gen.mime import get_type - -# Python Modules: -import datetime - -class PersonForm(forms.ModelForm): - class Meta: - model = Person - exclude = ["death", "birth", "handle", "birth_ref_index", - "death_ref_index", "families", "parent_families", - "cache"] - def save(self, *args, **kwargs): - self.instance.save_cache_q = kwargs.pop("save_cache", True) - return super().save(*args, **kwargs) - -class NameForm(forms.ModelForm): - class Meta: - model = Name - # Exclude these, so they don't get checked: - exclude = ["order", "calendar", "modifier", - "quality", - #"quality_estimated", "quality_calculated", - #"quality_interpreted", - "year1", "day1", "month1", - "sortval", "newyear", "person"] - # Add these because they are TextFields, which render as - # Textareas: - first_name = forms.CharField(label="Given", - required=False, - widget=TextInput(attrs={'size':'60'})) - title = forms.CharField(required=False, - widget=TextInput(attrs={'size':'15'})) - call = forms.CharField(label="Call", - required=False, - widget=TextInput(attrs={'size':'15'})) - nick = forms.CharField(label="Nick", - required=False, - widget=TextInput(attrs={'size':'15'})) - group_as = forms.CharField(label="Group as", - required=False, - widget=TextInput(attrs={'size':'30'})) - suffix = forms.CharField(required=False, - initial=' suffix ', - widget=TextInput(attrs={'size':'15', - 'style': 'font-style: italic; color: gray; ', - 'onFocus': """if (this.value == ' suffix ') { - this.value = ''; - } - this.style.color = "black"; - this.style.fontStyle = 'normal'; - """, - 'onBlur': """if (this.value == '') { - this.value = ' suffix '; - this.style.color = "gray"; - this.style.fontStyle = 'italic'; - } - """})) - -class NameFormFromPerson(NameForm): - class Meta: - model = Name - # Exclude these, so they don't get checked: - # Excludes sort_as and display_as - exclude = ["order", "calendar", "modifier", - "quality", - #"quality_estimated", "quality_calculated", - #"quality_interpreted", - "year1", "day1", "month1", - "sortval", "newyear", "person", - "group_as", "sort_as", "display_as"] - -class SurnameForm(forms.ModelForm): - class Meta: - model = Surname - exclude = ['name', 'order'] - - surname = forms.CharField(label="Surname", - required=False, - widget=TextInput(attrs={'size':'30'})) - - connector = forms.CharField(label="Connector", - required=False, - widget=TextInput(attrs={'size':'30'})) - - prefix = forms.CharField(label="Prefix", - required=False, - initial=' prefix ', - widget=TextInput(attrs={'size':'15', - 'style': 'font-style: italic; color: gray; ', - 'onFocus': """if (this.value == ' prefix ') { - this.value = ''; - } - this.style.color = "black"; - this.style.fontStyle = 'normal'; - """, - 'onBlur': """if (this.value == '') { - this.value = ' prefix '; - this.style.color = "gray"; - this.style.fontStyle = 'italic'; - } - """})) -class FamilyForm(forms.ModelForm): - class Meta: - model = Family - exclude = ["handle", "cache", "mother", "father"] - -class EventForm(forms.ModelForm): - class Meta: - model = Event - exclude = ["handle", "sortval", "month1", "year1", "day1", - "newyear", "calendar", "modifier", "quality", "cache", - "place"] - - def clean(self): - from gramps.webapp.utils import dp - data = super(EventForm, self).clean() - dobj = dp(data.get('text')) - if not dobj.is_valid(): - msg = "Invalid date format" - self._errors["date"] = self.error_class([msg]) - del data["text"] - return data - - def save(self, commit=True): - from gramps.webapp.utils import dp - from gramps.webapp.libdjango import DjangoInterface - dji = DjangoInterface() - model = super(EventForm, self).save(commit=False) - dobj = dp(self.cleaned_data['text']) - dji.add_date(model, dobj.serialize()) - if commit: - model.save() - return model - - text = forms.CharField(label="Date", - required=False, - widget=TextInput(attrs={'size':'45'})) - -class NoteForm(forms.ModelForm): - class Meta: - model = Note - exclude = ["handle", "text"] - - notetext = forms.CharField(label="Text", - widget=forms.widgets.Textarea(attrs={'rows':'10', 'cols': '80', 'class':'wysiwyg'})) - -class MediaForm(forms.ModelForm): - class Meta: - model = Media - exclude = ["handle", "sortval", "month1", "year1", "day1", - "newyear", "calendar", "modifier", "quality", "cache"] - - def clean(self): - from gramps.webapp.utils import dp - data = super(MediaForm, self).clean() - dobj = dp(data.get('text')) - if not dobj.is_valid(): - msg = "Invalid date format" - self._errors["date"] = self.error_class([msg]) - del data["text"] - return data - - def save(self, commit=True): - from gramps.webapp.utils import dp - from gramps.webapp.libdjango import DjangoInterface - dji = DjangoInterface() - model = super(MediaForm, self).save(commit=False) - model.mime = get_type(model.path) - dobj = dp(self.cleaned_data['text']) - dji.add_date(model, dobj.serialize()) - if commit: - model.save() - return model - - text = forms.CharField(label="Date", - required=False, - widget=TextInput(attrs={'size':'70'})) - desc = forms.CharField(label="Title", - required=False, - widget=TextInput(attrs={'size':'70'})) - path = forms.CharField(label="Path", - required=False, - widget=TextInput(attrs={'size':'70'})) - -class CitationForm(forms.ModelForm): - class Meta: - model = Citation - exclude = ["handle", "sortval", "month1", "year1", "day1", - "newyear", "calendar", "modifier", "quality", "cache"] - - def clean(self): - from gramps.webapp.utils import dp - data = super(CitationForm, self).clean() - dobj = dp(data.get('text')) - if not dobj.is_valid(): - msg = "Invalid date format" - self._errors["date"] = self.error_class([msg]) - del data["text"] - return data - - def save(self, commit=True): - from gramps.webapp.utils import dp - from gramps.webapp.libdjango import DjangoInterface - dji = DjangoInterface() - model = super(CitationForm, self).save(commit=False) - dobj = dp(self.cleaned_data['text']) - dji.add_date(model, dobj.serialize()) - if commit: - model.save() - return model - - text = forms.CharField(label="Date", - required=False, - widget=TextInput(attrs={'size':'70'})) - -class SourceForm(forms.ModelForm): - class Meta: - model = Source - exclude = ["handle", "cache"] - -class PlaceForm(forms.ModelForm): - class Meta: - model = Place - exclude = ["handle", "cache"] - - title = forms.CharField(label="Title", - required=False, - widget=TextInput(attrs={'size':'70'})) - long = forms.CharField(label="Longitude", - required=False, - widget=TextInput(attrs={'size':'70'})) - lat = forms.CharField(label="Latitude", - required=False, - widget=TextInput(attrs={'size':'70'})) - -class RepositoryForm(forms.ModelForm): - class Meta: - model = Repository - exclude = ["handle", "cache"] - - name = forms.CharField(label="Name", - required=False, - widget=TextInput(attrs={'size':'70'})) - -class TagForm(forms.ModelForm): - class Meta: - model = Tag - exclude = ["handle"] - - name = forms.CharField(label="Name", - required=False, - widget=TextInput(attrs={'size':'70'})) - -class EventRefForm(forms.ModelForm): - class Meta: - model = EventRef - -class LogForm(forms.ModelForm): - error_css_class = 'error' - - class Meta: - model = Log - fields = ["reason"] - - reason = forms.CharField(label="Reason for change", - widget=forms.widgets.Textarea(attrs={'rows':'2', - 'cols': '65'})) -class PickForm(forms.Form): - picklist = forms.ChoiceField() - def __init__(self, label, item, order_by, *args, **kwargs): - super(PickForm, self).__init__(*args, **kwargs) - self.fields['picklist'].choices = \ - [("", "---------")] + [(p.handle, p) for p in item.objects.all() \ - .order_by(*order_by)] diff --git a/gramps/webapp/grampsdb/models.py b/gramps/webapp/grampsdb/models.py deleted file mode 100644 index 2d61b2e71..000000000 --- a/gramps/webapp/grampsdb/models.py +++ /dev/null @@ -1,1229 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 B. Malengier -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" -All of the models for the grampsdb Django data schema. -This requires initial data for all of the Types, which -is loaded by the fixtures/initial_data.json, which is -created by init.py. -""" -from django.db import models -from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes import generic - -from gramps.gen.lib.date import Date as GDate, Today -from gramps.gen.utils.id import create_id, create_uid - -from gramps.webapp.grampsdb.profile import Profile - -import pickle -import base64 - -#--------------------------------------------------------------------------- -# -# Support functions -# -#--------------------------------------------------------------------------- - -def get_type_from_name(the_type, name): - """ - Gets the type for a given name. - >>> get_type_from_name(GenderType, "Female") - - >>> get_type_from_name(GenderType, "Male") - - """ - return the_type.objects.get(name=name) - -def get_type(the_type, data, get_or_create=False): - """ - Gets the default row for a given Type and data. Data is - a pair, (VAL, NAME). VAL + NAME should be unique. Will create - one if it doesn't already exist. - """ - if type(data) == type(1): - return the_type.objects.get(val=data) - elif data[0] == the_type._CUSTOM or get_or_create: - (obj, new) = the_type.objects.get_or_create(val=data[0], - name=data[1]) - return obj - else: - return the_type.objects.get(val=data[0]) - -def get_default_type(the_type): - """ - Gets the default database object for a given GrampsType. - """ - val, name = the_type._DEFAULT - return the_type.objects.get(val=val, name=name) - -def get_default_type_value(the_type): - """ - Gets the default value for a given gen.lib.GrampsType. - """ - return [x for x in the_type._DATAMAP if x[0] == the_type._DEFAULT][0] - -def get_datamap(grampsclass): - return sorted([(x[0],x[2]) for x in grampsclass._DATAMAP], - key=lambda item: item[1]) - -#--------------------------------------------------------------------------- -# -# Types -# -#--------------------------------------------------------------------------- - -class mGrampsType(models.Model): - """ - The abstract base class for all types. - Types are enumerated integers. One integer corresponds with custom, then - custom_type holds the type name - """ - class Meta: abstract = True - - _CUSTOM = 0 - _DEFAULT = 0 - _DATAMAP = [] - - name = models.CharField(max_length=40) - - def __str__(self): - return str(self.name) - - def get_default_type(self): - """ return a tuple default (val,name) """ - return self._DEFAULT - - def __len__(self): - """ For use as a sequence for getting (val, name) """ - return 2 - - def __getitem__(self, pos): - """ for getting the parts as if they were the original tuples.""" - if pos == 0: - return self.val - elif pos == 1: - return self.name - else: - raise IndexError("type index is out of range (use 0 or 1)") - -class NameType(mGrampsType): - from gramps.gen.lib.nametype import NameType - _DATAMAP = get_datamap(NameType) - _CUSTOM = NameType._CUSTOM - _DEFAULT = get_default_type_value(NameType) - val = models.IntegerField('name type', choices=_DATAMAP, blank=False) - -class NameOriginType(mGrampsType): - from gramps.gen.lib.nameorigintype import NameOriginType - _DATAMAP = get_datamap(NameOriginType) - _CUSTOM = NameOriginType._CUSTOM - _DEFAULT = get_default_type_value(NameOriginType) - val = models.IntegerField('name origin type', choices=_DATAMAP, blank=False) - -class AttributeType(mGrampsType): - from gramps.gen.lib.attrtype import AttributeType - _DATAMAP = get_datamap(AttributeType) - _CUSTOM = AttributeType._CUSTOM - _DEFAULT = get_default_type_value(AttributeType) - val = models.IntegerField('attribute type', choices=_DATAMAP, blank=False) - -class UrlType(mGrampsType): - from gramps.gen.lib.urltype import UrlType - _DATAMAP = get_datamap(UrlType) - _CUSTOM = UrlType._CUSTOM - _DEFAULT = get_default_type_value(UrlType) - val = models.IntegerField('url type', choices=_DATAMAP, blank=False) - -class ChildRefType(mGrampsType): - from gramps.gen.lib.childreftype import ChildRefType - _DATAMAP = get_datamap(ChildRefType) - _CUSTOM = ChildRefType._CUSTOM - _DEFAULT = get_default_type_value(ChildRefType) - val = models.IntegerField('child reference type', choices=_DATAMAP, - blank=False) - -class RepositoryType(mGrampsType): - from gramps.gen.lib.repotype import RepositoryType - _DATAMAP = get_datamap(RepositoryType) - _CUSTOM = RepositoryType._CUSTOM - _DEFAULT = get_default_type_value(RepositoryType) - val = models.IntegerField('repository type', choices=_DATAMAP, blank=False) - -class PlaceType(mGrampsType): - from gramps.gen.lib.placetype import PlaceType - _DATAMAP = get_datamap(PlaceType) - _CUSTOM = PlaceType._CUSTOM - _DEFAULT = get_default_type_value(PlaceType) - val = models.IntegerField('place type', choices=_DATAMAP, blank=False) - -class EventType(mGrampsType): - from gramps.gen.lib.eventtype import EventType - _DATAMAP = get_datamap(EventType) - _CUSTOM = EventType._CUSTOM - _DEFAULT = get_default_type_value(EventType) - BIRTH = 12 - DEATH = 13 - val = models.IntegerField('event type', choices=_DATAMAP, blank=False) - - def get_url(self): - return "/event/?search=type%%3D%s" % self.name - - def get_link(self): - return "%s" % (self.get_url(), self.name) - - -class FamilyRelType(mGrampsType): - from gramps.gen.lib.familyreltype import FamilyRelType - _DATAMAP = get_datamap(FamilyRelType) - _CUSTOM = FamilyRelType._CUSTOM - _DEFAULT = get_default_type_value(FamilyRelType) - val = models.IntegerField('family relation type', choices=_DATAMAP, - blank=False) - -class SourceMediaType(mGrampsType): - from gramps.gen.lib.srcmediatype import SourceMediaType - _DATAMAP = get_datamap(SourceMediaType) - _CUSTOM = SourceMediaType._CUSTOM - _DEFAULT = get_default_type_value(SourceMediaType) - val = models.IntegerField('source medium type', choices=_DATAMAP, - blank=False) - -class EventRoleType(mGrampsType): - from gramps.gen.lib.eventroletype import EventRoleType - _DATAMAP = get_datamap(EventRoleType) - _CUSTOM = EventRoleType._CUSTOM - _DEFAULT = get_default_type_value(EventRoleType) - val = models.IntegerField('event role type', choices=_DATAMAP, blank=False) - -class NoteType(mGrampsType): - from gramps.gen.lib.notetype import NoteType - _DATAMAP = get_datamap(NoteType) - _CUSTOM = NoteType._CUSTOM - _DEFAULT = get_default_type_value(NoteType) - val = models.IntegerField('note type', choices=_DATAMAP, blank=False) - -class StyledTextTagType(mGrampsType): - from gramps.gen.lib.styledtexttagtype import StyledTextTagType - _DATAMAP = get_datamap(StyledTextTagType) - _CUSTOM = None - _DEFAULT = None - val = models.IntegerField('styled text tag type', choices=_DATAMAP, blank=False) - -class GenderType(mGrampsType): - _DATAMAP = [(2, 'Unknown'), (1, 'Male'), (0, 'Female')] - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('gender type', choices=_DATAMAP, blank=False) - -class LdsType(mGrampsType): - _DATAMAP = [(0, "Baptism" ), - (1, "Endowment" ), - (2, "Seal to Parents"), - (3, "Seal to Spouse"), - (4, "Confirmation")] - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('lds type', choices=_DATAMAP, blank=False) - -class LdsStatus(mGrampsType): - _DATAMAP = [(0, "None"), - (1, "BIC"), - (2, "Canceled"), - (3, "Child"), - (4, "Cleared"), - (5, "Completed"), - (6, "Dns"), - (7, "Infant"), - (8, "Pre 1970"), - (9, "Qualified"), - (10, "DNSCAN"), - (11, "Stillborn"), - (12, "Submitted"), - (13, "Uncleared")] - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('lds status', choices=_DATAMAP, blank=False) - -class NameFormatType(mGrampsType): - _DATAMAP = [(0, "Default format"), - (1, "Surname, Given Patronymic"), - (2, "Given Surname"), - (3, "Patronymic, Given"),] - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('Name formats', choices=_DATAMAP, blank=False) - -class CalendarType(mGrampsType): - CAL_GREGORIAN = 0 # CODE - CAL_JULIAN = 1 - CAL_HEBREW = 2 - CAL_FRENCH = 3 - CAL_PERSIAN = 4 - CAL_ISLAMIC = 5 - CAL_SWEDISH = 6 - - _DATAMAP = [(CAL_GREGORIAN, "Gregorian"), - (CAL_JULIAN, "Julian"), - (CAL_HEBREW, "Hebrew"), - (CAL_FRENCH, "French Republican"), - (CAL_PERSIAN, "Persian"), - (CAL_ISLAMIC, "Islamic"), - (CAL_SWEDISH, "Swedish")] - - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('Calendar', choices=_DATAMAP, blank=False) - -class DateModifierType(mGrampsType): - MOD_NONE = 0 # CODE - MOD_BEFORE = 1 - MOD_AFTER = 2 - MOD_ABOUT = 3 - MOD_RANGE = 4 - MOD_SPAN = 5 - MOD_TEXTONLY = 6 - - _DATAMAP = [(MOD_NONE, ""), - (MOD_BEFORE, "Before"), - (MOD_AFTER, "After"), - (MOD_ABOUT, "About"), - (MOD_RANGE, "Range"), - (MOD_SPAN, "Span"), - (MOD_TEXTONLY, "Text only")] - - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('Date modifier', choices=_DATAMAP, blank=False) - -class DateNewYearType(mGrampsType): - NEWYEAR_JAN1 = 0 # CODE - NEWYEAR_MAR1 = 1 - NEWYEAR_MAR25 = 2 - NEWYEAR_SEP1 = 3 - - _DATAMAP = [(NEWYEAR_JAN1, ""), - (NEWYEAR_MAR1, "March 1"), - (NEWYEAR_MAR25, "March 25"), - (NEWYEAR_SEP1, "September 1")] - - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('New Year start date', choices=_DATAMAP, blank=False) - -class ThemeType(mGrampsType): - _DATAMAP = list(enumerate(["Web_Mainz.css", - "Web_Basic-Ash.css", - "Web_Basic-Cypress.css", - "Web_Nebraska.css", - "Web_Basic-Lilac.css", - "Web_Print-Default.css", - "Web_Basic-Peach.css", - "Web_Visually.css", - "Web_Basic-Spruce.css",])) - _DEFAULT = _DATAMAP[0] - val = models.IntegerField('Theme', choices=_DATAMAP, blank=False) - -#--------------------------------------------------------------------------- -# -# Support definitions -# -#--------------------------------------------------------------------------- - -class DateObject(models.Model): - class Meta: abstract = True - - calendar = models.IntegerField(default=0) - modifier = models.IntegerField(default=0) - quality = models.IntegerField(default=0) - #quality_estimated = models.BooleanField() - #quality_calculated = models.BooleanField() - #quality_interpreted = models.BooleanField() - day1 = models.IntegerField(default=0) - month1 = models.IntegerField(default=0) - year1 = models.IntegerField(default=0) - slash1 = models.BooleanField(default=False) - day2 = models.IntegerField(blank=True, null=True) - month2 = models.IntegerField(blank=True, null=True) - year2 = models.IntegerField(blank=True, null=True) - slash2 = models.NullBooleanField(blank=True, null=True) - text = models.CharField(max_length=80, blank=True) - sortval = models.IntegerField(default=0) - newyear = models.IntegerField(default=0) - - def set_date_from_datetime(self, date_time, text=""): - """ - Sets Date fields from an object that has year, month, and day - properties. - """ - y, m, d = date_time.year, date_time.month, date_time.day - self.set_ymd(self, y, m, d, text=text) - - def set_date_from_ymd(self, y, m, d, text=""): - """ - Sets Date fields from a year, month, and day. - """ - gdate = GDate(y, m, d) - gdate.text = text - self.set_date_from_gdate(gdate) - - def set_date_from_gdate(self, gdate): - """ - Sets Date fields from a Gramps date object. - """ - (self.calendar, self.modifier, self.quality, dateval, self.text, - self.sortval, self.newyear) = gdate.serialize() - if dateval is None: - (self.day1, self.month1, self.year1, self.slash1) = 0, 0, 0, False - (self.day2, self.month2, self.year2, self.slash2) = 0, 0, 0, False - elif len(dateval) == 8: - (self.day1, self.month1, self.year1, self.slash1, - self.day2, self.month2, self.year2, self.slash2) = dateval - elif len(dateval) == 4: - (self.day1, self.month1, self.year1, self.slash1) = dateval - (self.day2, self.month2, self.year2, self.slash2) = 0, 0, 0, False - -#--------------------------------------------------------------------------- -# -# Primary Tables -# -#--------------------------------------------------------------------------- - -class Config(models.Model): - """ - All of the meta config items for the entire system. - """ - setting = models.CharField('config setting', max_length=50) - description = models.TextField('description', null=True, blank=True) - value_type = models.CharField('type of value', max_length=80) - value = models.TextField('value') - - def __str__(self): - return str(self.setting) - -class Tag(models.Model): - handle = models.CharField(max_length=19, unique=True) - gramps_id = models.TextField(blank=True, null=True) - last_saved = models.DateTimeField('last changed', auto_now=True) - last_changed = models.DateTimeField('last changed', null=True, - blank=True) # user edits - last_changed_by = models.TextField(blank=True, null=True) - - name = models.TextField('name') - color = models.CharField(max_length=13, blank=True, null=True) # "#000000000000" # Black - priority = models.IntegerField('priority', blank=True, null=True) - cache = models.TextField(blank=True, null=True) - dji = None - - def __str__(self): - return str(self.name) - - def get_url(self): - return "/tag/%s" % self.handle - - def get_link(self): - return "%s" % (self.get_url(), self.name) - - def make_cache(self): - from gramps.webapp.libdjango import DjangoInterface - if self.dji is None: - self.dji = DjangoInterface() - raw = self.dji.get_tag(self) - return str(base64.encodebytes(pickle.dumps(raw)), "utf-8") - - def from_cache(self): - return pickle.loads(base64.decodebytes(bytes(self.cache, "utf-8"))) - - def save_cache(self): - cache = self.make_cache() - if cache != self.cache: - self.cache = cache - models.Model.save(self) - - def save(self, *args, **kwargs): - if "save_cache" in kwargs: - self.save_cache_q = kwargs.pop("save_cache") - if hasattr(self, "save_cache_q") and self.save_cache_q: - # Tag doesn't have a cache - self.cache = self.make_cache() - models.Model.save(self, *args, **kwargs) # save to db - - -# Just the following have tag lists: -# --------------------------------- -#src/gen/lib/family.py -#src/gen/lib/mediaobj.py -#src/gen/lib/note.py -#src/gen/lib/person.py - -class PrimaryObject(models.Model): - """ - Common attribute of all primary objects with key on the handle - """ - class Meta: abstract = True - - ## Fields: - id = models.AutoField(primary_key=True) - handle = models.CharField(max_length=19, unique=True) - gramps_id = models.CharField('ID', max_length=25, blank=True) - last_saved = models.DateTimeField('last changed', auto_now=True) - last_changed = models.DateTimeField('last changed', null=True, - blank=True) # user edits - last_changed_by = models.TextField(blank=True, null=True) - - private = models.BooleanField('private', default=True) - public = models.BooleanField('public', default=True) - #attributes = models.ManyToManyField("Attribute", blank=True, null=True) - cache = models.TextField(blank=True, null=True) - tags = models.ManyToManyField('Tag', blank=True, null=True) - dji = None - save_cache_q = False - - def __str__(self): - return "%s: %s" % (self.__class__.__name__, - self.gramps_id) - - def get_url(self): - return "/%s/%s" % (self.__class__.__name__.lower(), - self.handle) - - def get_tag_list(self): - return [tag.handle for tag in self.tags.all()] - - def make_cache(self): - from gramps.webapp.libdjango import DjangoInterface - if self.dji is None: - self.dji = DjangoInterface() - - if isinstance(self, Person): - raw = self.dji.get_person(self) - elif isinstance(self, Family): - raw = self.dji.get_family(self) - elif isinstance(self, Place): - raw = self.dji.get_place(self) - elif isinstance(self, Media): - raw = self.dji.get_media(self) - elif isinstance(self, Source): - raw = self.dji.get_source(self) - elif isinstance(self, Citation): - raw = self.dji.get_citation(self) - elif isinstance(self, Repository): - raw = self.dji.get_repository(self) - elif isinstance(self, Note): - raw = self.dji.get_note(self) - elif isinstance(self, Event): - raw = self.dji.get_event(self) - elif isinstance(self, Tag): - raw = self.dji.get_tag(self) - else: - raise Exception("Don't know how to get raw '%s'" % type(item)) - return str(base64.encodebytes(pickle.dumps(raw)), "utf-8") - - def from_cache(self): - return pickle.loads(base64.decodebytes(bytes(self.cache, "utf-8"))) - - def save_cache(self): - cache = self.make_cache() - if cache != self.cache: - self.cache = cache - models.Model.save(self) - - def save(self, *args, **kwargs): - if "save_cache" in kwargs: - self.save_cache_q = kwargs.pop("save_cache") - if self.save_cache_q: - self.cache = self.make_cache() - models.Model.save(self, *args, **kwargs) # save to db - -class MyFamilies(models.Model): - person = models.ForeignKey("Person") - family = models.ForeignKey("Family") - order = models.PositiveIntegerField(default=1) - -class MyParentFamilies(models.Model): - person = models.ForeignKey("Person") - family = models.ForeignKey("Family") - order = models.PositiveIntegerField(default=1) - -class Person(PrimaryObject): - """ - The model for the person object - """ - gender_type = models.ForeignKey('GenderType', verbose_name="Gender") - probably_alive = models.BooleanField("Probably alive", default=True) - families = models.ManyToManyField('Family', blank=True, null=True, through="MyFamilies") - parent_families = models.ManyToManyField('Family', - related_name="parent_families", - blank=True, null=True, - through='MyParentFamilies') - #addresses = models.ManyToManyField('Address', null=True, blank=True) - references = generic.GenericRelation('PersonRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - birth = models.ForeignKey("Event", related_name="birth", blank=True, null=True) - death = models.ForeignKey("Event", related_name="death", blank=True, null=True) - - birth_ref_index = models.IntegerField("Birth Reference Index", default=-1) - death_ref_index = models.IntegerField("Death Reference Index", default=-1) - - # Others keys here: - # .name_set - # .address_set - # .lds_set - # .url_set - - def get_primary_name(self): - """ - Return the preferred name of a person. - """ - try: - return self.name_set.get(preferred=True) - except: - return "" - - def __str__(self): - return "%s [%s]" % (self.get_primary_name(), self.gramps_id) - - def get_selection_string(self): - return self.name_set.get(preferred=True).get_selection_string() - - def save(self, *args, **kwargs): - from gramps.webapp.utils import probably_alive - compute_probably_alive = self.save_cache_q - PrimaryObject.save(self, *args, **kwargs) - # expensive! only do this if also saving cache - if compute_probably_alive: - pa = probably_alive(self.handle) - if self.probably_alive != pa: - self.probably_alive = pa - PrimaryObject.save(self, *args, **kwargs) - -class Family(PrimaryObject): - father = models.ForeignKey('Person', related_name="father_ref", - null=True, blank=True) - mother = models.ForeignKey('Person', related_name="mother_ref", - null=True, blank=True) - family_rel_type = models.ForeignKey('FamilyRelType', verbose_name="Type") - - #lds_list = models.ManyToManyField('Lds', null=True, blank=True) - - # Others keys here: - # .lds_set - - def get_children(self): - """ - Return all children from this family, in order. - """ - obj_type = ContentType.objects.get_for_model(self) - childrefs = ChildRef.objects.filter(object_id=self.id, - object_type=obj_type).order_by("order") - return [childref.ref_object for childref in childrefs] - - def __str__(self): - father = self.father.get_primary_name() if self.father else "No father" - mother = self.mother.get_primary_name() if self.mother else "No mother" - return "%s and %s" % (father, mother) - -class Citation(DateObject, PrimaryObject): - confidence = models.IntegerField(blank=True, null=True) - page = models.CharField("Volume/Page", max_length=50, blank=True, null=True) - source = models.ForeignKey('Source', null=True, blank=True) - references = generic.GenericRelation('CitationRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - - def __str__(self): - return "[%s] (%s, %s) to %s" % (self.gramps_id, - self.confidence, - self.page, - self.source) - - # Other keys here: - # .datamap_set - -class Source(PrimaryObject): - title = models.CharField(max_length=50, blank=True, null=True) - author = models.CharField(max_length=50, blank=True, null=True) - pubinfo = models.CharField("Pub. info.", max_length=50, blank=True, null=True) - abbrev = models.CharField("Abbreviation", max_length=50, blank=True, null=True) - - def __str__(self): - return "[%s] %s" % (self.gramps_id, - self.title) - - # Other keys here: - # .datamap_set - -class Event(DateObject, PrimaryObject): - event_type = models.ForeignKey('EventType', verbose_name="Type") - description = models.CharField('description', max_length=50, blank=True) - place = models.ForeignKey('Place', null=True, blank=True) - references = generic.GenericRelation('EventRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - - def __str__(self): - return "[%s] (%s) %s" % (self.gramps_id, - self.event_type, - self.description) - -class Repository(PrimaryObject): - repository_type = models.ForeignKey('RepositoryType', verbose_name="Type") - name = models.TextField(blank=True) - #addresses = models.ManyToManyField('Address', null=True, blank=True) - references = generic.GenericRelation('RepositoryRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - #url_list = models.ManyToManyField('Url', null=True, blank=True) - - def __str__(self): - return "[%s] %s" % (self.gramps_id, self.name) - - # Others keys here: - # .address_set - # .url_set - -class Place(DateObject, PrimaryObject): - place_type = models.ForeignKey('PlaceType', verbose_name="Type") - title = models.TextField(blank=True) - #locations = models.ManyToManyField('Location', null=True, blank=True) - long = models.TextField(blank=True) - lat = models.TextField(blank=True) - name = models.TextField(blank=True) - lang = models.TextField(blank=True) - code = models.TextField(blank=True) # zipcode - - #url_list = models.ManyToManyField('Url', null=True, blank=True) - - def get_selection_string(self): - return "%s [%s]" % (self.title, self.gramps_id) - - def __str__(self): - return str(self.title) - - # Others keys here: - # .url_set - # .location_set - -class Media(DateObject, PrimaryObject): - path = models.TextField(blank=True) - mime = models.TextField(blank=True, null=True) - desc = models.TextField("Title", blank=True) - checksum = models.TextField(blank=True) - references = generic.GenericRelation('MediaRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - - def __str__(self): - return str(self.desc) - -class Note(PrimaryObject): - note_type = models.ForeignKey('NoteType', verbose_name="Type") - text = models.TextField(blank=True) - preformatted = models.BooleanField('preformatted', default=True) - references = generic.GenericRelation('NoteRef', #related_name="refs", - content_type_field="object_type", - object_id_field="object_id") - - def __str__(self): - return str(self.gramps_id) - -#--------------------------------------------------------------------------- -# -# Secondary Tables -# -#--------------------------------------------------------------------------- - -class SecondaryObject(models.Model): - """ - We use interlinked objects, secondary object is the table for primary - objects to refer to when linking to non primary objects - """ - class Meta: abstract = True - - private = models.BooleanField(default=True) - last_saved = models.DateTimeField('last changed', auto_now=True) - last_changed = models.DateTimeField('last changed', null=True, - blank=True) # user edits - last_changed_by = models.TextField(blank=True, null=True) - order = models.PositiveIntegerField(default=1) - -class Surname(models.Model): - """ - Surname table, which links to name. - """ - name_origin_type = models.ForeignKey('NameOriginType', - verbose_name="Origin", - related_name="name_origin_code", - default=2) - surname = models.TextField(blank=True) - prefix = models.TextField(blank=True) - primary = models.BooleanField('Primary surname?', default=True) - connector = models.TextField(blank=True) - name = models.ForeignKey("Name") - order = models.PositiveIntegerField() - - def __str__(self): - return str(self.surname) - - def get_url(self): - # /person/handle/name/1/surname/2 - return "/person/%s/name/%s/surname/%s" % (self.name.person.handle, - self.name.order, - self.order) - -class Name(DateObject, SecondaryObject): - name_type = models.ForeignKey('NameType', verbose_name="Type", - related_name="name_code", - default=2) - preferred = models.BooleanField('Preferred name?', default=True) - first_name = models.TextField(blank=True) - suffix = models.TextField(blank=True) - title = models.TextField(blank=True) - call = models.TextField(blank=True) - nick = models.TextField(blank=True) - famnick = models.TextField(blank=True) - group_as = models.TextField(blank=True) - sort_as = models.ForeignKey('NameFormatType', - related_name="sort_as", - default=1) - display_as = models.ForeignKey('NameFormatType', - related_name="display_as", - default=1) - ## Key: - person = models.ForeignKey("Person") - _sanitized = False - - def __str__(self): - try: - surname = self.surname_set.get(primary=True) - except: - surname = "[No primary surname]" - return "%s, %s" % (surname, self.first_name) - - def get_selection_string(self): - try: - surname = self.surname_set.get(primary=True) - except: - surname = "[No primary surname]" - return "%s, %s [%s]" % (surname, self.first_name, self.person.gramps_id) - - @staticmethod - def get_dummy(): - name = Name() - #name. - - def sanitize(self): - if not self._sanitized: - self._sanitized = True - if self.person.probably_alive: - self.first_name = "[Living]" - self.nick = "" - self.call = "" - self.group_as = "" - self.title = "" - - def make_surname_list(self): - return [(x.surname, x.prefix, x.primary, - tuple(x.name_origin_type), x.connector) for x in - self.surname_set.all()] - - def get_url(self): - # /person/handle/name/1 - return "/person/%s/name/%s" % (self.person.handle, self.order) - -class Lds(DateObject, SecondaryObject): - """ - BAPTISM = 0 - ENDOWMENT = 1 - SEAL_TO_PARENTS = 2 - SEAL_TO_SPOUSE = 3 - CONFIRMATION = 4 - - DEFAULT_TYPE = BAPTISM - - - STATUS_NONE = 0 - STATUS_BIC = 1 - STATUS_CANCELED = 2 - STATUS_CHILD = 3 - STATUS_CLEARED = 4 - STATUS_COMPLETED = 5 - STATUS_DNS = 6 - STATUS_INFANT = 7 - STATUS_PRE_1970 = 8 - STATUS_QUALIFIED = 9 - STATUS_DNS_CAN = 10 - STATUS_STILLBORN = 11 - STATUS_SUBMITTED = 12 - STATUS_UNCLEARED = 13 - - DEFAULT_STATUS = STATUS_NONE - """ - lds_type = models.ForeignKey('LdsType') - place = models.ForeignKey('Place', null=True) - famc = models.ForeignKey('Family', related_name="famc", null=True) - temple = models.TextField(blank=True) - status = models.ForeignKey('LdsStatus') - - person = models.ForeignKey("Person", null=True, blank=True) - family = models.ForeignKey("Family", null=True, blank=True) - -class Markup(models.Model): - note = models.ForeignKey('Note') - styled_text_tag_type = models.ForeignKey('StyledTextTagType') - order = models.PositiveIntegerField() - string = models.TextField(blank=True, null=True) - start_stop_list = models.TextField(default="[]") - -class SourceAttribute(models.Model): - key = models.CharField(max_length=80, blank=True) - value = models.CharField(max_length=80, blank=True) - source = models.ForeignKey("Source") - private = models.BooleanField(default=True) - order = models.PositiveIntegerField() - -class CitationAttribute(models.Model): - key = models.CharField(max_length=80, blank=True) - value = models.CharField(max_length=80, blank=True) - citation = models.ForeignKey("Citation") - private = models.BooleanField(default=True) - order = models.PositiveIntegerField() - -class Address(DateObject, SecondaryObject): - #locations = models.ManyToManyField('Location', null=True) - person = models.ForeignKey('Person', null=True, blank=True) - repository = models.ForeignKey('Repository', null=True, blank=True) - - # Others keys here: - # .location_set - - -class Location(models.Model): - street = models.TextField(blank=True) - locality = models.TextField(blank=True) - city = models.TextField(blank=True) - county = models.TextField(blank=True) - state = models.TextField(blank=True) - country = models.TextField(blank=True) - postal = models.TextField(blank=True) - phone = models.TextField(blank=True) - parish = models.TextField(blank=True, null=True) - order = models.PositiveIntegerField() - - place = models.ForeignKey("Place", null=True, blank=True) - address = models.ForeignKey("Address", null=True, blank=True) - -class Url(models.Model): - private = models.BooleanField('private url?', default=True) - path = models.TextField(blank=True, null=True) - desc = models.TextField(blank=True, null=True) - url_type = models.ForeignKey('UrlType') - order = models.PositiveIntegerField() - - person = models.ForeignKey("Person", null=True, blank=True) - place = models.ForeignKey("Place", null=True, blank=True) - repository = models.ForeignKey("Repository", null=True, blank=True) - -class Attribute(models.Model): - private = models.BooleanField('private attribute?', default=True) - attribute_type = models.ForeignKey('AttributeType') - value = models.TextField(blank=True, null=True) - - object_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - attribute_of = generic.GenericForeignKey("object_type", "object_id") - -## consider using: -## URLField - -#--------------------------------------------------------------------------- -# -# Reference Objects -# -#--------------------------------------------------------------------------- - -class BaseRef(models.Model): - class Meta: abstract = True - - object_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() - referenced_by = generic.GenericForeignKey("object_type", "object_id") - - order = models.PositiveIntegerField() - last_saved = models.DateTimeField('last changed', auto_now=True) - last_changed = models.DateTimeField('last changed', null=True) # user edits - last_changed_by = models.TextField(blank=True, null=True) - - #attributes = models.ManyToManyField("Attribute", null=True) - private = models.BooleanField(default=True) - - def get_url(self): - # /person/3536453463/reference/event/2 - ref_by = self.object_type.model_class().objects.get(id=self.object_id) - ref_to = self.get_reference_to() - return "/%s/%s/reference/%s/%s" % (ref_by.__class__.__name__.lower(), - ref_by.handle, - ref_to.__class__.__name__.lower(), - self.order) -class Log(BaseRef): - log_type = models.CharField(max_length=10) # edit, delete, add - reason = models.TextField() # must be filled in - cache = models.TextField(blank=True, null=True) - - def __str__(self): - return "%s: %s on %s by %s" % (self.log_type, - self.referenced_by, - self.last_changed, - self.last_changed_by) - -class NoteRef(BaseRef): - ref_object = models.ForeignKey('Note') - - def get_reference_to(self): - return self.ref_object - - def __str__(self): - return "NoteRef to " + str(self.ref_object) - -class EventRef(BaseRef): - ref_object = models.ForeignKey('Event') - role_type = models.ForeignKey('EventRoleType') - - def __str__(self): - return str(self.ref_object) - - def get_reference_to(self): - return self.ref_object - - def get_url(self): - # /person/3536453463/reference/event/2 - ref_by = self.object_type.model_class().objects.get(id=self.object_id) - ref_to = self.ref_object - return "/%s/%s/reference/%s/%s" % (ref_by.__class__.__name__.lower(), - ref_by.handle, - ref_to.__class__.__name__.lower(), - self.order) - -class RepositoryRef(BaseRef): - ref_object = models.ForeignKey('Repository') - source_media_type = models.ForeignKey('SourceMediaType') - call_number = models.CharField(max_length=50) - - def get_reference_to(self): - return self.ref_object - - def __str__(self): - return "RepositoryRef to " + str(self.ref_object) - -class PlaceRef(BaseRef, DateObject): - ref_object = models.ForeignKey('Place') - - def get_reference_to(self): - return self.ref_object - - def __str__(self): - return "PlaceRef to " + str(self.ref_object) - -class PersonRef(BaseRef): - ref_object = models.ForeignKey('Person') - description = models.CharField(max_length=50, blank=True, null=True) - - def get_reference_to(self): - return self.ref_object - - def __str__(self): - return "PersonRef to " + str(self.ref_object) - -class CitationRef(BaseRef): - citation = models.ForeignKey('Citation') - - def __str__(self): - return "CitationRef to " + str(self.citation) - - def get_reference_to(self): - return self.citation - -class ChildRef(BaseRef): - father_rel_type = models.ForeignKey('ChildRefType', - related_name="child_father_rel") - mother_rel_type = models.ForeignKey('ChildRefType', - related_name="child_mother_rel") - ref_object = models.ForeignKey('Person') - - def get_reference_to(self): - return self.ref_object - - def get_url(self): - # FIXME: go to child reference - return "/person/%s" % self.ref_object.handle - - def __str__(self): - return "ChildRef to " + str(self.ref_object) - -class MediaRef(BaseRef): - x1 = models.IntegerField() - y1 = models.IntegerField() - x2 = models.IntegerField() - y2 = models.IntegerField() - ref_object = models.ForeignKey('Media') - - def get_reference_to(self): - return self.ref_object - - def __str__(self): - return "MediaRef to " + str(self.ref_object) - -class Report(models.Model): - gramps_id = models.TextField(blank=True, null=True) - name = models.TextField(blank=True, null=True) - handle = models.TextField(blank=True, null=True) # report_id - report_type = models.TextField(blank=True, null=True) - options = models.TextField(blank=True, null=True) - - def __str__(self): - return str(self.name) - -class Result(models.Model): - name = models.TextField(blank=True, null=True) - filename = models.TextField(blank=True, null=True) - run_on = models.DateTimeField('run on', auto_now=True) - run_by = models.TextField('run by', blank=True, null=True) - status = models.TextField(blank=True, null=True) - - def __str__(self): - return str(self.name) - -class Metadata(models.Model): - setting = models.TextField(blank=False, null=False) - value = models.BinaryField() - -class GenderStats(models.Model): - # GenderStats (name, female, male, unknown) - name = models.TextField(null=False) - female = models.IntegerField(blank=False) - male = models.IntegerField(blank=False) - unknown = models.IntegerField(blank=False) - -class Reference(models.Model): - # Reference (obj_handle, obj_class, ref_handle, ref_class) - obj_handle = models.CharField(max_length=19) - obj_class = models.TextField(null=False) - ref_handle = models.CharField(max_length=19) - ref_class = models.TextField(null=False) - -class NameGroup(models.Model): - # NameGroup table (name, grouping) - name = models.TextField(blank=False, null=False) - grouping = models.TextField(null=False) - -TABLES = [ - ("abstract", mGrampsType), - ("type", NameType), - ("type", NameOriginType), - ("type", NameFormatType), - ("type", AttributeType), - ("type", UrlType), - ("type", ChildRefType), - ("type", RepositoryType), - ("type", PlaceType), - ("type", EventType), - ("type", FamilyRelType), - ("type", SourceMediaType), - ("type", EventRoleType), - ("type", NoteType), - ("type", GenderType), - ("type", LdsType), - ("type", LdsStatus), - ("type", ThemeType), - ("type", StyledTextTagType), - ("abstract", DateObject), - ("abstract", PrimaryObject), - ("primary", Person), - ("primary", Family), - ("primary", Citation), - ("primary", Source), - ("primary", Event), - ("primary", Repository), - ("primary", Place), - ("primary", Media), - ("primary", Note), - ("primary", Tag), - ("abstract", SecondaryObject), - ("secondary", Attribute), - ("secondary", SourceAttribute), - ("secondary", CitationAttribute), - ("secondary", Name), - ("secondary", Surname), - ("secondary", Lds), - ("secondary", Markup), - ("secondary", Address), - ("secondary", Location), - ("secondary", Url), - ("abstract", BaseRef), - ("ref", CitationRef), - ("ref", NoteRef), - ("ref", EventRef), - ("ref", RepositoryRef), - ("ref", PlaceRef), - ("ref", PersonRef), - ("ref", ChildRef), - ("ref", MediaRef), - ("ref", MyFamilies), - ("ref", MyParentFamilies), - ("system", Config), - ("system", Report), - ("system", Result), - ] - -def no_style(): - """Returns a Django Style object that has no colors.""" - class dummy(object): - def __getattr__(self, attr): - return lambda x: x - return dummy() - -def clear_tables(*categories): - """ - Clear the entries of categories of tables. Category is: - "abstract", "type", "ref", "system", "primary" and "secondary". - """ - # FIXME: I don't think this works anymore... - from django.db import connection, transaction - cursor = connection.cursor() - flush_tables = [] - for (category, model) in get_tables(*categories): - flush_tables.append(model._meta.db_table) - # tables = connection.introspection.table_names() - # flush_tables = [table for table in tables if not table.endswith("type")] - statements = connection.ops.sql_flush(no_style(), - flush_tables, - connection.introspection.sequence_list()) - for statement in statements: - cursor.execute(statement) - transaction.commit_unless_managed() - -def table_stats(*categories): - """ - Shows the record counts for each table category. - """ - tables = get_tables(*categories) - tables.sort() - for pair in tables: - print(("%-25s" % pair[1].__name__), ":", \ - pair[1].objects.all().count()) - -def get_tables(*categories): - return [pair for pair in TABLES if (pair[0] in categories) or - ("all" in categories) and pair[0] != "abstract"] - diff --git a/gramps/webapp/grampsdb/profile.py b/gramps/webapp/grampsdb/profile.py deleted file mode 100644 index d5ee77202..000000000 --- a/gramps/webapp/grampsdb/profile.py +++ /dev/null @@ -1,43 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -from django.db import models -from django.db.models.signals import post_save -from django.contrib.auth.models import User - -def save_profile(sender, instance, created, **kwargs): - """ - Creates the profile when the user gets created. - """ - if created: - profile = Profile(user=instance) - profile.save() - -class Profile(models.Model): - """ - Used to save additional information of a user, such as - themes, bookmarks, etc. - """ - user = models.OneToOneField(User, related_name="profile") - theme_type = models.ForeignKey("ThemeType", default=1) # The default is a pk? - - def __str__(self): - return str(self.user) - -post_save.connect(save_profile, sender=User) diff --git a/gramps/webapp/grampsdb/sql/childref.sql b/gramps/webapp/grampsdb/sql/childref.sql deleted file mode 100644 index 1f89f22e1..000000000 --- a/gramps/webapp/grampsdb/sql/childref.sql +++ /dev/null @@ -1,3 +0,0 @@ - -CREATE INDEX grampsdb_childref_object_id_object_type_id - ON grampsdb_childref (object_id, object_type_id); diff --git a/gramps/webapp/grampsdb/sql/eventref.sql b/gramps/webapp/grampsdb/sql/eventref.sql deleted file mode 100644 index 8620c9d7a..000000000 --- a/gramps/webapp/grampsdb/sql/eventref.sql +++ /dev/null @@ -1,3 +0,0 @@ - -CREATE INDEX grampsdb_eventref_object_id_object_type_id - ON grampsdb_eventref (object_id, object_type_id); diff --git a/gramps/webapp/grampsdb/sql/name.sql b/gramps/webapp/grampsdb/sql/name.sql deleted file mode 100644 index 8b1378917..000000000 --- a/gramps/webapp/grampsdb/sql/name.sql +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gramps/webapp/grampsdb/sql/noteref.sql b/gramps/webapp/grampsdb/sql/noteref.sql deleted file mode 100644 index 47bdaf396..000000000 --- a/gramps/webapp/grampsdb/sql/noteref.sql +++ /dev/null @@ -1,3 +0,0 @@ - -CREATE INDEX grampsdb_noteref_object_id_object_type_id - ON grampsdb_noteref (object_id, object_type_id); diff --git a/gramps/webapp/grampsdb/sql/sourceref.sql b/gramps/webapp/grampsdb/sql/sourceref.sql deleted file mode 100644 index 2784b568e..000000000 --- a/gramps/webapp/grampsdb/sql/sourceref.sql +++ /dev/null @@ -1,3 +0,0 @@ - -CREATE INDEX grampsdb_sourceref_object_id_object_type_id - ON grampsdb_sourceref (object_id, object_type_id); diff --git a/gramps/webapp/grampsdb/templatetags/__init__.py b/gramps/webapp/grampsdb/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/gramps/webapp/grampsdb/templatetags/my_tags.py b/gramps/webapp/grampsdb/templatetags/my_tags.py deleted file mode 100644 index ee533f727..000000000 --- a/gramps/webapp/grampsdb/templatetags/my_tags.py +++ /dev/null @@ -1,230 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -# webapp/grampsdb/templatetags/my_tags.py - -import re - -from django import template -from django.template import Library -from django.utils.safestring import mark_safe -from gramps.webapp.utils import * -from gramps.webapp.grampsdb.views import VIEWS -import gramps.webapp.utils - -#escape = lambda text: text - -register = Library() - -def eval_template_exp(item, context): - """ - Wrapper to allow negation of variables in templates. Use - "!variable". - """ - if item.var.startswith("!"): - return not template.Variable(item.var[1:]).resolve(context) - else: - return item.resolve(context) - -class TemplateNode(template.Node): - def __init__(self, args, var_name, func): - self.args = list(map(template.Variable, args)) - self.var_name = var_name - self.func = func - - def render(self, context): - value = self.func(*[eval_template_exp(item, context) - for item in self.args]) - if self.var_name: - context[self.var_name] = value - return '' - else: - return value - -def parse_tokens(tokens): - items = tokens.split_contents() - # {% tag_name arg1 arg2 arg3 as variable %} - # {% tag_name arg1 arg2 arg3 %} - tag_name = items[0] - if "as" == items[-2]: - var_name = items[-1] - args = items[1:-2] - else: - var_name = None - args = items[1:] - return (tag_name, args, var_name) - -def make_tag(func): - def do_func(parser, tokens): - tag_name, args, var_name = parse_tokens(tokens) - return TemplateNode(args, var_name, func) - return do_func - -for filter_name in util_filters: - func = getattr(gramps.webapp.utils, filter_name) - func.is_safe = True - register.filter(filter_name, func) - -for tag_name in util_tags: - func = getattr(gramps.webapp.utils, tag_name) - register.tag(tag_name, make_tag(func)) - -probably_alive.is_safe = True -register.filter('probably_alive', probably_alive) - -format_number.is_safe = True -register.filter('format_number', format_number) - -table_count.is_safe = True -register.filter('table_count', table_count) - -person_get_birth_date.is_safe = True -register.filter('person_get_birth_date', person_get_birth_date) - -person_get_death_date.is_safe = True -register.filter('person_get_death_date', person_get_death_date) - -display_date.is_safe = True -register.filter('display_date', display_date) - -person_get_event.is_safe = True -register.filter('person_get_events', person_get_event) - -def preview(text, width=40): - text = text.replace("\n", " ") - #return escape(text[:width]) - if len(text) > width: - return text[:width] + "..." - return text -#preview.is_safe = True -register.filter('preview', preview) - -make_name.is_safe = True -register.filter('make_name', make_name) - -def preferred(person): - try: - name = person.name_set.get(preferred=True) - except: - name = None - return name -preferred.is_safe = True -register.filter('preferred', preferred) - -def missing(data): - if data.strip() == "": - return "[Missing]" - #return escape(data) - return data -#missing.is_safe = True -register.filter('missing', missing) - -def getViewName(item): - for view in VIEWS: - if view[1] == item: - return view[0] - if item == "name": - return "Names" - return "Unknown View" - -def breadcrumb(path, arg=None): - if arg: - path = path.replace("{0}", arg) - retval = "" - for item in path.split("||"): - p, name = item.split("|", 1) - if retval != "": - retval += " > " - retval += '%s' % (p.strip(), name.strip()) - return "

%s

" % retval -breadcrumb.is_safe = True -register.filter('breadcrumb', breadcrumb) - -def format(string, arg0=None, arg1=None, arg2=None, arg3=None, arg4=None, arg5=None, arg6=None): - try: - if arg0 is None: - return string - elif arg1 is None: - return string % arg0 - elif arg2 is None: - return string % (arg0, arg1) - elif arg3 is None: - return string % (arg0, arg1, arg2) - elif arg4 is None: - return string % (arg0, arg1, arg2, arg3) - elif arg5 is None: - return string % (arg0, arg1, arg2, arg3, arg4) - elif arg6 is None: - return string % (arg0, arg1, arg2, arg3, arg4, arg5) - else: - return string % (arg0, arg1, arg2, arg3, arg4, arg5, arg6) - except: - return string -format.is_safe = True -register.simple_tag(format) - -def make_args(search, page): - return gramps.webapp.utils.build_args(search=search, page=page) -make_args.is_safe = True -register.simple_tag(make_args) - -def format_color(color): - return color[0:3] + color[5:7] + color[9:11] -format_color.is_safe = True -register.filter("format_color", format_color) - -def currentSection(view1, view2): # tview, menu - if view1.strip().lower() in [view[1] for view in VIEWS] and view2 == "browse": - return "class=CurrentSection" - elif view1.strip().lower() == view2.strip().lower(): - return "class=CurrentSection" - return "" -currentSection.is_safe = True -register.filter('currentSection', currentSection) - -def row_count(row, page): - return row + (page.number - 1) * page.paginator.per_page - -register.filter('row_count', row_count) - -def table_header(context, headers = None): - # add things for the header here - if headers: - context["headers"] = headers - return context - -register.inclusion_tag('table_header.html', - takes_context=True)(table_header) - -def paginator(context, adjacent_pages=2): - """ - To be used in conjunction with the object_list generic view. - - Adds pagination context variables for use in displaying first, adjacent and - last page links in addition to those created by the object_list generic - view. - - """ - results_this_page = context["page"].object_list.count() - context.update({'results_this_page': results_this_page,}) - return context - -register.inclusion_tag('paginator.html', - takes_context=True)(paginator) diff --git a/gramps/webapp/grampsdb/view/__init__.py b/gramps/webapp/grampsdb/view/__init__.py deleted file mode 100644 index 2743b061d..000000000 --- a/gramps/webapp/grampsdb/view/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2012 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -from .person import * -from .family import * -from .event import * -from .note import * -from .media import * -from .citation import * -from .source import * -from .place import * -from .repository import * -from .tag import * diff --git a/gramps/webapp/grampsdb/view/citation.py b/gramps/webapp/grampsdb/view/citation.py deleted file mode 100644 index a66968d3f..000000000 --- a/gramps/webapp/grampsdb/view/citation.py +++ /dev/null @@ -1,141 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search -from gramps.webapp.grampsdb.models import Citation -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_citation(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Citation") - context["tviews"] = _("Citations") - context["action"] = "view" - view_template = "view_citation_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick citation", - Citation, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick citation", - Citation, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Citation.objects.get(handle=ref_handle) - dji.add_citation_ref_default(parent_obj, ref_obj) - parent_obj.save_cache() # rebuild cache - return redirect("/%s/%s%s#tab-citations" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "add": - source = Source(gramps_id=dji.get_next_id(Source, "S")) - sourceform = SourceForm(instance=source) - sourceform.model = source - citation = Citation(source=source, gramps_id=dji.get_next_id(Citation, "C")) - citationform = CitationForm(instance=citation) - citationform.model = citation - elif act in ["view", "edit"]: - citation = Citation.objects.get(handle=handle) - citationform = CitationForm(instance=citation) - citationform.model = citation - source = citation.source - sourceform = SourceForm(instance=source) - sourceform.model = source - elif act == "save": - citation = Citation.objects.get(handle=handle) - citationform = CitationForm(request.POST, instance=citation) - citationform.model = citation - if citationform.is_valid(): - update_last_changed(citation, request.user.username) - citation = citationform.save() - act = "view" - else: - act = "edit" - elif act == "create": - source = Source(handle=create_id()) - sourceform = SourceForm(request.POST, instance=source) - sourceform.model = source - citation = Citation(handle=create_id(), source=source) - citationform = CitationForm(request.POST, instance=citation) - citationform.model = citation - if citationform.is_valid() and sourceform.is_valid(): - update_last_changed(source, request.user.username) - source = sourceform.save() - citation.source = source - update_last_changed(citation, request.user.username) - citation = citationform.save(save_cache=False) - source.save_cache() - citation.save_cache() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_citation_ref(obj, citation.handle) - obj.save_cache() - return redirect("/%s/%s#tab-citations" % (item, handle)) - act = "view" - else: - act = "add" - elif act == "delete": - citation = Citation.objects.get(handle=handle) - citation.delete() - return redirect("/citation/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["citationform"] = citationform - context["sourceform"] = sourceform - context["object"] = citation - context["citation"] = citation - context["source"] = source - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/event.py b/gramps/webapp/grampsdb/view/event.py deleted file mode 100644 index 9948f8acb..000000000 --- a/gramps/webapp/grampsdb/view/event.py +++ /dev/null @@ -1,227 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search, db -from gramps.webapp.grampsdb.models import Event, EventType, EventRef, EventRoleType, Person -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface, lookup_role_index -from gramps.webapp.djangodb import DbDjango -from gramps.gen.datehandler import displayer, parser - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() -dd = displayer.display -dp = parser.parse - -def delete_event(event): - obj_type = ContentType.objects.get_for_model(Person) - # First, get those items we need to update: - event_refs = EventRef.objects.filter( - ref_object=event, - object_type=obj_type, - role_type=get_type_from_name(EventRoleType, "Primary") - ) - people = [] - for event_ref in event_refs: - try: - person = Person.objects.get(id=event_ref.object_id) - except: - print("Warning: Corrupt reference in delete_event: %s" % event_ref) - continue - people.append(person) - # Remove links to birth/death: - for person in people: - if person.death == event: - person.death = None - person.save() - elif person.birth == event: - person.birth = None - person.save() - # Now, delete the event: - event.delete() - # Now, update all of the people: - for person in people: - recheck_birth_death_refs(person) - person.save() - -def check_event(event): - obj_type = ContentType.objects.get_for_model(Person) - # First, get those items we need to update: - event_refs = EventRef.objects.filter( - ref_object=event, - object_type=obj_type, - role_type=get_type_from_name(EventRoleType, "Primary") - ) - people = [] - for event_ref in event_refs: - try: - person = Person.objects.get(id=event_ref.object_id) - except: - print("Warning: Corrupt reference in delete_event: %s" % event_ref) - continue - recheck_birth_death_refs(person) - person.save() - -def recheck_birth_death_refs(person): - """ - Reset birth/death references. Need to save later. - """ - all_events = dji.get_event_ref_list(person) - obj_type = ContentType.objects.get_for_model(person) - # Update Birth event references: - events = EventRef.objects.filter( - object_id=person.id, - object_type=obj_type, - role_type=get_type_from_name(EventRoleType, "Primary"), - ref_object__event_type__val=EventType.BIRTH).order_by("order") - if events: - person.birth = events[0].ref_object - new_index = lookup_role_index(EventType.BIRTH, all_events) - if person.birth_ref_index != new_index: - person.birth_ref_index = new_index - else: - person.birth = None - person.birth_ref_index = -1 - # Update Death event references: - events = EventRef.objects.filter( - object_id=person.id, - object_type=obj_type, - role_type=get_type_from_name(EventRoleType, "Primary"), - ref_object__event_type__val=EventType.DEATH).order_by("order") - if events: - person.death = events[0].ref_object - new_index = lookup_role_index(EventType.DEATH, all_events) - if person.death_ref_index != new_index: - person.death_ref_index = new_index - else: - person.death = None - person.death_ref_index = -1 - -def process_event(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Event") - context["tviews"] = _("Events") - context["action"] = "view" - view_template = "view_event_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick event", - Event, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick event", - Event, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Event.objects.get(handle=ref_handle) - dji.add_event_ref_default(parent_obj, ref_obj) - if item == "person": # then need to recheck birth/death indexes: - recheck_birth_death_refs(parent_obj) - parent_obj.save(save_cache=False) - parent_obj.save_cache() - return redirect("/%s/%s%s#tab-events" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "add": - event = Event(gramps_id=dji.get_next_id(Event, "E")) - eventform = EventForm(instance=event) - eventform.model = event - elif act in ["view", "edit"]: - event = Event.objects.get(handle=handle) - genlibevent = db.get_event_from_handle(handle) - if genlibevent: - date = genlibevent.get_date_object() - event.text = dd(date) - eventform = EventForm(instance=event) - eventform.model = event - elif act == "save": - event = Event.objects.get(handle=handle) - eventform = EventForm(request.POST, instance=event) - eventform.model = event - if eventform.is_valid(): - update_last_changed(event, request.user.username) - event = eventform.save() - # Check any person that might be referenced to see if - # birth/death issues changed: - check_event(event) - event.save() - act = "view" - else: - act = "edit" - elif act == "create": - event = Event(handle=create_id()) - eventform = EventForm(request.POST, instance=event) - eventform.model = event - if eventform.is_valid(): - update_last_changed(event, request.user.username) - event = eventform.save() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_event_ref_default(obj, event) - if item == "person": # then need to recheck birth/death indexes: - recheck_birth_death_refs(obj) - obj.save(save_cache=False) - obj.save_cache() - return redirect("/%s/%s#tab-events" % (item, handle)) - act = "view" - else: - act = "add" - elif act == "delete": - event = Event.objects.get(handle=handle) - delete_event(event) - return redirect("/event/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["eventform"] = eventform - context["object"] = event - context["event"] = event - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/family.py b/gramps/webapp/grampsdb/view/family.py deleted file mode 100644 index 4d4a67f31..000000000 --- a/gramps/webapp/grampsdb/view/family.py +++ /dev/null @@ -1,201 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search -from gramps.webapp.grampsdb.models import Family -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface -from gramps.gen.utils.id import create_id - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_family(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Family") - context["tviews"] = _("Familes") - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - # Adds a person to an existing family - item, handle = add_to - context["pickform"] = PickForm("Pick family", - Family, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = "person" - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick family", - Family, - (), - request.POST) - if pickform.data["picklist"]: - person = Person.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Family.objects.get(handle=ref_handle) - if item == "child": - dji.add_child_ref_default(ref_obj, person) # add person to family - #person.parent_families.add(ref_obj) # add family to child - pfo = MyParentFamilies(person=person, family=ref_obj, - order=len(person.parent_families.all())+1) - pfo.save() - elif item == "spouse": - if person.gender_type.name == "Female": - ref_obj.mother = person - elif person.gender_type.name == "Male": - ref_obj.father = person - else: - ref_obj.father = person # FIXME: Unknown gender, add to open - #person.families.add(ref_obj) # add family to person - pfo = MyFamilies(person=person, family=ref_obj, - order=len(person.families.all())+1) - pfo.save() - ref_obj.save() - person.save() - return redirect("/%s/%s%s#tab-references" % ("person", handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = "person" - return render_to_response("pick.html", context) - elif act == "add": - family = Family( - gramps_id=dji.get_next_id(Family, "F"), - family_rel_type=FamilyRelType.objects.get( - val=FamilyRelType._DEFAULT[0])) - if add_to: - what, phandle = add_to - person = Person.objects.get(handle=phandle) - gender = person.gender_type.name # Male, Female, Unknown - if what == "spouse": - if gender == "Male": - family.father = person - elif gender == "Female": - family.mother = person - else: # You have to pick one! - family.father = person - elif what == "child": - pass # FIXME: can't show child in table? - # Table from children_table - else: # unknown what! - raise Exception("can't add_to: '%s'" % what) - familyform = FamilyForm(instance=family) - familyform.model = family - elif act in ["view", "edit"]: - family = Family.objects.get(handle=handle) - familyform = FamilyForm(instance=family) - familyform.model = family - elif act == "save": # editing an existing family - family = Family.objects.get(handle=handle) - old_family_mother = family.mother - old_family_father = family.father - familyform = FamilyForm(request.POST, instance=family) - familyform.model = family - if familyform.is_valid(): - update_last_changed(family, request.user.username) - family = familyform.save() - # Remove family from previous mother/father if changed - if familyform.cleaned_data["mother"] != old_family_mother and old_family_mother: - MyFamilies.objects.get(person=old_family_mother, family=family).delete() - if familyform.cleaned_data["father"] != old_family_father and old_family_father: - MyFamilies.objects.get(person=old_family_father, family=family).delete() - # Add family to newly selected mother/father if needed: - if familyform.cleaned_data["mother"]: - if family not in familyform.cleaned_data["mother"].families.all(): - #family.mother.families.add(family) - pfo = MyFamilies(person=familyform.cleaned_data["mother"], family=family, - order=len(familyform.cleaned_data["mother"].families.all())+1) - pfo.save() - if familyform.cleaned_data["father"]: - if family not in familyform.cleaned_data["father"].families.all(): - #family.father.families.add(family) - pfo = MyFamilies(person=family.father, family=family, - order=len(familyform.cleaned_data["father"].families.all())+1) - pfo.save() - familyform.save() - act = "view" - else: - act = "edit" - elif act == "create": - family = Family(family_rel_type=FamilyRelType.objects.get( - val=FamilyRelType._DEFAULT[0]), - handle=create_id()) - familyform = FamilyForm(request.POST, instance=family) - familyform.model = family - if familyform.is_valid(): - update_last_changed(family, request.user.username) - family = familyform.save() - if family.mother: - #family.mother.families.add(family) - pfo = MyFamilies(person=family.mother, family=family, - order=len(family.mother.families.all())+1) - pfo.save() - if family.father: - #family.father.families.add(family) - pfo = MyFamilies(person=family.father, family=family, - order=len(family.father.families.all())+1) - pfo.save() - family.save_cache() - if add_to: # add child or spouse to family - item, handle = add_to - person = Person.objects.get(handle=handle) - if item == "child": - dji.add_child_ref_default(family, person) # add person to family - ##person.parent_families.add(family) # add family to child - pfo = MyParentFamilies(person=person, family=family, - order=len(person.parent_families.all())+1) - pfo.save() - #elif item == "spouse": - # already added by selecting - person.save() - return redirect("/%s/%s%s#tab-references" % ("person", handle, build_search(request))) - act = "view" - else: - act = "add" - elif act == "delete": - family = Family.objects.get(handle=handle) - family.delete() - return redirect("/family/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["familyform"] = familyform - context["object"] = family - context["family"] = family - context["action"] = act - view_template = "view_family_detail.html" - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/media.py b/gramps/webapp/grampsdb/view/media.py deleted file mode 100644 index 56ef9c264..000000000 --- a/gramps/webapp/grampsdb/view/media.py +++ /dev/null @@ -1,203 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search -from gramps.webapp.grampsdb.models import Media -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface -from gramps.gen.config import config - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext -from django.http import HttpResponse - -## Other Python Modules -try: - from PIL import Image - NEW_PIL = [int(i) for i in Image.VERSION.split(".")] >= [1, 1, 7] - if not NEW_PIL: - from . import png -except: - print("WARNING: No PIL installed or available") - NEW_PIL = False - from . import png -import os - -## Globals -dji = DjangoInterface() - -def pb2image(pb): - width, height = pb.get_width(), pb.get_height() - return Image.fromstring("RGB", (width,height), pb.get_pixels()) - -def process_media(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Media") - context["tviews"] = _("Media") - context["action"] = "view" - view_template = "view_media_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick media", - Media, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick media", - Media, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Media.objects.get(handle=ref_handle) - dji.add_media_ref_default(parent_obj, ref_obj) - parent_obj.save_cache() # rebuild cache - return redirect("/%s/%s%s#tab-media" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "full": - media = Media.objects.get(handle=handle) - media_type, media_ext = media.mime.split("/", 1) - # FIXME: This should be absolute: - folder = Config.objects.get(setting="behavior.addmedia-image-dir").value - # FIXME: media.path should not have any .. for security - response = HttpResponse(content_type=media.mime) - if NEW_PIL or media_ext != "png": - image = Image.open("%s/%s" % (folder, media.path)) - image.save(response, media_ext) - else: - # FIXME: older PIL 1.1.6 cannot read interlaced PNG files - reader = png.Reader(filename="%s/%s" % (folder, media.path)) - x, y, pixels, meta = reader.asDirect() - image = png.Image(pixels, meta) - image.save(response) - return response - elif act == "thumbnail": - media = Media.objects.get(handle=handle) - media_type, media_ext = media.mime.split("/", 1) - # FIXME: This should be absolute: - folder = Config.objects.get(setting="behavior.addmedia-image-dir").value - # FIXME: media.path should not have any .. for security - response = HttpResponse(content_type=media.mime) - if os.path.exists("%s/thumbnail/%s" % (folder, media.path)): - if NEW_PIL or media_ext != "png": - image = Image.open("%s/thumbnail/%s" % (folder, media.path)) - image.save(response, media_ext) - else: - # FIXME: older PIL 1.1.6 cannot read interlaced PNG files - reader = png.Reader(filename="%s/thumbnail/%s" % (folder, media.path)) - x, y, pixels, meta = reader.asDirect() - image = png.Image(pixels, meta) - image.save(response) - else: - try: - os.makedirs("%s/thumbnail" % folder) - except: - pass - if NEW_PIL or media_ext != "png": - image = Image.open("%s/%s" % (folder, media.path)) - image.thumbnail((300,300), Image.ANTIALIAS) - image.save("%s/thumbnail/%s" % (folder, media.path), media_ext) - image.save(response, media_ext) - else: - # FIXME: older PIL 1.1.6 cannot read interlaced PNG files - reader = png.Reader(filename="%s/%s" % (folder, media.path)) - x, y, pixels, meta = reader.asDirect() - meta["interlace"] = False - image = png.Image(pixels, meta) - image.save("/tmp/%s" % media.path) - # Now open in PIL to rescale - image = Image.open("/tmp/%s" % media.path) - image.thumbnail((300,300), Image.ANTIALIAS) - image.save("%s/thumbnail/%s" % (folder, media.path), media_ext) - image.save(response, media_ext.upper()) - return response - elif act == "add": - media = Media(gramps_id=dji.get_next_id(Media, "M")) - mediaform = MediaForm(instance=media) - mediaform.model = media - elif act in ["view", "edit"]: - media = Media.objects.get(handle=handle) - mediaform = MediaForm(instance=media) - mediaform.model = media - elif act == "save": - media = Media.objects.get(handle=handle) - mediaform = MediaForm(request.POST, instance=media) - mediaform.model = media - if mediaform.is_valid(): - update_last_changed(media, request.user.username) - media = mediaform.save() - act = "view" - else: - act = "edit" - elif act == "create": - media = Media(handle=create_id()) - mediaform = MediaForm(request.POST, instance=media) - mediaform.model = media - if mediaform.is_valid(): - update_last_changed(media, request.user.username) - media = mediaform.save(save_cache=False) - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_media_ref_default(obj, media) - obj.save_cache() - media.save_cache() - return redirect("/%s/%s#tab-gallery" % (item, handle)) - else: - media.save_cache() - act = "view" - else: - act = "add" - elif act == "delete": - media = Media.objects.get(handle=handle) - media.delete() - return redirect("/media/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["mediaform"] = mediaform - context["object"] = media - context["media"] = media - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/note.py b/gramps/webapp/grampsdb/view/note.py deleted file mode 100644 index 0b9442a6c..000000000 --- a/gramps/webapp/grampsdb/view/note.py +++ /dev/null @@ -1,153 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, StyledNoteFormatter, parse_styled_text, build_search, db -from gramps.webapp.grampsdb.models import Note -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface -from gramps.webapp.djangodb import DbDjango - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() -snf = StyledNoteFormatter(db) - -# add a note to a person: -# /note/add/person/c51759195496de06da3ca5ba2c1 - -def process_note_on_name(request, action, handle, order): - # add, edit, delete - raise Exception("testing") - -def process_note(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Note") - context["tviews"] = _("Notes") - context["action"] = "view" - view_template = "view_note_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick note", - Note, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick note", - Note, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Note.objects.get(handle=ref_handle) - dji.add_note_ref(parent_obj, ref_obj) - parent_obj.save_cache() # rebuild cache - return redirect("/%s/%s%s#tab-notes" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "add": - note = Note(gramps_id=dji.get_next_id(Note, "N")) - notetext = "" - noteform = NoteForm(instance=note, initial={"notetext": notetext}) - noteform.model = note - elif act in ["view", "edit"]: - note = Note.objects.get(handle=handle) - genlibnote = db.get_note_from_handle(note.handle) - notetext = snf.format(genlibnote) - noteform = NoteForm(instance=note, initial={"notetext": notetext}) - noteform.model = note - elif act == "save": - note = Note.objects.get(handle=handle) - notetext = "" - noteform = NoteForm(request.POST, instance=note, initial={"notetext": notetext}) - noteform.model = note - if noteform.is_valid(): - update_last_changed(note, request.user.username) - notedata = parse_styled_text(noteform.data["notetext"]) - note.text = notedata[0] - note = noteform.save() - dji.save_note_markup(note, notedata[1]) - note.save_cache() - notetext = noteform.data["notetext"] - act = "view" - else: - notetext = noteform.data["notetext"] - act = "edit" - elif act == "create": - note = Note(handle=create_id()) - notetext = "" - noteform = NoteForm(request.POST, instance=note, initial={"notetext": notetext}) - noteform.model = note - if noteform.is_valid(): - update_last_changed(note, request.user.username) - notedata = parse_styled_text(noteform.data["notetext"]) - note.text = notedata[0] - note = noteform.save() - dji.save_note_markup(note, notedata[1]) - note.save_cache() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_note_ref(obj, note) - obj.save_cache() - return redirect("/%s/%s#tab-notes" % (item, handle)) - notetext = noteform.data["notetext"] - act = "view" - else: - notetext = noteform.data["notetext"] - act = "add" - elif act == "delete": - # FIXME: delete markup too for this note - note = Note.objects.get(handle=handle) - note.delete() - return redirect("/note/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["noteform"] = noteform - context["object"] = note - context["notetext"] = notetext - context["note"] = note - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/person.py b/gramps/webapp/grampsdb/view/person.py deleted file mode 100644 index dfe760a29..000000000 --- a/gramps/webapp/grampsdb/view/person.py +++ /dev/null @@ -1,529 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search, make_log -from gramps.webapp.grampsdb.models import Person, Name, Surname -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.http import Http404 -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -## Functions -def check_order(request, person): - """ - Check for proper ordering 1..., and for a preferred name. - """ - order = 1 - preferred = False - for name in person.name_set.all().order_by("order"): - if name.preferred: - preferred = True - if name.order != order: - name.order = order - update_last_changed(name, request.user.username) - name.save() - order += 1 - if not preferred: - name = person.name_set.get(order=1) - name.preferred = True - update_last_changed(name, request.user.username) - name.save() - -def check_primary(surname, surnames): - """ - Check for a proper primary surname. - """ - if surname.primary: - # then all rest should not be: - for s in surnames: - if s.primary: - s.primary = False - s.save() - else: - # then one of them should be - ok = False - for s in surnames: - if s.id != surname.id: - if s.primary: - ok = True - break - else: - s.primary = False - s.save() - ok = True - break - if not ok: - surname.primary = True - -def check_preferred(request, name, person): - """ - Check for a proper preferred name. - """ - names = [] - if person: - names = person.name_set.all() - if name.preferred: - # then all reast should not be: - for s in names: - if s.preferred and s.id != name.id: - s.preferred = False - update_last_changed(s, request.user.username) - s.save() - else: - # then one of them should be - ok = False - for s in names: - if s.id != name.id: - if s.preferred: - ok = True - break - else: - s.preferred = False - update_last_changed(s, request.user.username) - s.save() - ok = True - break - if not ok: - name.preferred = True - -def process_surname(request, handle, order, sorder, act="view"): - #import pdb; pdb.set_trace() - # /sdjhgsdjhdhgsd/name/1/surname/1 (view) - # /sdjhgsdjhdhgsd/name/1/surname/add - # /sdjhgsdjhdhgsd/name/1/surname/2/[edit|view|add|delete] - - if sorder == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - person = Person.objects.get(handle=handle) - name = person.name_set.get(order=order) - - if act in ["view", "edit"]: - surname = name.surname_set.get(order=sorder) - if act == "edit": - surname.prefix = make_empty(True, surname.prefix, " prefix ") - elif act in ["delete"]: - surnames = name.surname_set.all().order_by("order") - if len(surnames) > 1: - neworder = 1 - for surname in surnames: - if surname.order != neworder: - surname.order = neworder - surname.save() - neworder += 1 - elif surname.order == int(sorder): - surname.delete() - else: - neworder += 1 - else: - request.user.message_set.create(message="You can't delete the only surname") - return redirect("/person/%s/name/%s%s#tab-surnames" % (person.handle, name.order, - build_search(request))) - elif act in ["add"]: - surname = Surname(name=name, primary=False, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0])) - surname.prefix = make_empty(True, surname.prefix, " prefix ") - elif act == "create": - surnames = name.surname_set.all().order_by("order") - sorder = 1 - for surname in surnames: - sorder += 1 - surname = Surname(name=name, primary=True, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0]), - order=sorder) - sf = SurnameForm(request.POST, instance=surname) - sf.model = surname - if sf.is_valid(): - surname.prefix = ssf.cleaned_data["prefix"] if sf.cleaned_data["prefix"] != " prefix " else "" - surname = sf.save(commit=False) - check_primary(surname, surnames) - surname.save() - person.save_cache() - return redirect("/person/%s/name/%s/surname/%s%s#tab-surnames" % - (person.handle, name.order, sorder, - build_search(request))) - act = "add" - surname.prefix = make_empty(True, surname.prefix, " prefix ") - elif act == "save": - surname = name.surname_set.get(order=sorder) - sf = SurnameForm(request.POST, instance=surname) - sf.model = surname - if sf.is_valid(): - surname.prefix = ssf.cleaned_data["prefix"] if sf.cleaned_data["prefix"] != " prefix " else "" - surname = sf.save(commit=False) - check_primary(surname, name.surname_set.all().exclude(order=surname.order)) - surname.save() - person.save_cache() - return redirect("/person/%s/name/%s/surname/%s%s#tab-surnames" % - (person.handle, name.order, sorder, - build_search(request))) - act = "edit" - surname.prefix = make_empty(True, surname.prefix, " prefix ") - # else, edit again - else: - raise Exception("unknown act: '%s'" % act) - - sf = SurnameForm(instance=surname) - sf.model = surname - - context = RequestContext(request) - context["action"] = act - context["tview"] = _("Surname") - context["handle"] = handle - context["id"] = id - context["person"] = person - context["object"] = person - context["surnameform"] = sf - context["order"] = name.order - context["sorder"] = sorder - view_template = 'view_surname_detail.html' - return render_to_response(view_template, context) - -def process_name(request, handle, order, act="view"): - if order == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - ### Process act: - if act in "view": - pf, nf, sf, person = get_person_forms(handle, order=order) - name = nf.model - elif act == "edit": - pf, nf, sf, person = get_person_forms(handle, order=order) - name = nf.model - elif act == "delete": - person = Person.objects.get(handle=handle) - name = person.name_set.filter(order=order) - names = person.name_set.all() - if len(names) > 1: - name.delete() - check_order(request, person) - else: - request.user.message_set.create(message = "Can't delete only name.") - return redirect("/person/%s%s#tab-names" % (person.handle, - build_search(request))) - elif act == "add": # add name - person = Person.objects.get(handle=handle) - name = Name(person=person, - preferred=False, - display_as=NameFormatType.objects.get(val=NameFormatType._DEFAULT[0]), - sort_as=NameFormatType.objects.get(val=NameFormatType._DEFAULT[0]), - name_type=NameType.objects.get(val=NameType._DEFAULT[0])) - nf = NameForm(instance=name) - nf.model = name - surname = Surname(name=name, - primary=True, - order=1, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0])) - sf = SurnameForm(request.POST, instance=surname) - sf.model = surname - elif act == "create": - # make new data - person = Person.objects.get(handle=handle) - name = Name(preferred=False) - next_order = max([name.order for name in person.name_set.all()]) + 1 - surname = Surname(name=name, - primary=True, - order=next_order, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0])) - # combine with user data: - nf = NameForm(request.POST, instance=name) - name.id = None # FIXME: why did this get set to an existing name? Should be new. Remove from form? - name.preferred = False - nf.model = name - sf = SurnameForm(request.POST, instance=surname) - sf.model = surname - if nf.is_valid() and sf.is_valid(): - # name.preferred and surname.primary get set False in the above is_valid() - # person = pf.save() - # Process data: - name = nf.save(commit=False) - name.person = person - update_last_changed(name, request.user.username) - # Manually set any data: - name.suffix = nf.cleaned_data["suffix"] if nf.cleaned_data["suffix"] != " suffix " else "" - name.preferred = False # FIXME: why is this False? Remove from form? - name.order = next_order - name.save() - # Process data: - surname = sf.save(commit=False) - surname.name = name - # Manually set any data: - surname.prefix = sf.cleaned_data["prefix"] if sf.cleaned_data["prefix"] != " prefix " else "" - surname.primary = True # FIXME: why is this False? Remove from form? - surname.save() - person.save_cache() - return redirect("/person/%s/name/%s%s#tab-surnames" % (person.handle, name.order, - build_search(request))) - else: - act = "add" - elif act == "save": - # look up old data: - person = Person.objects.get(handle=handle) - oldname = person.name_set.get(order=order) - oldsurname = oldname.surname_set.get(primary=True) - # combine with user data: - pf = PersonForm(request.POST, instance=person) - pf.model = person - nf = NameForm(request.POST, instance=oldname) - nf.model = oldname - sf = SurnameForm(request.POST, instance=oldsurname) - sf.model = oldsurname - if nf.is_valid() and sf.is_valid(): - # name.preferred and surname.primary get set False in the above is_valid() - # person = pf.save() - # Process data: - oldname.person = person - name = nf.save() - # Manually set any data: - name.suffix = nf.cleaned_data["suffix"] if nf.cleaned_data["suffix"] != " suffix " else "" - name.preferred = True # FIXME: why is this False? Remove from form? - update_last_changed(name, request.user.username) - check_preferred(request, name, person) - name.save() - # Process data: - oldsurname.name = name - surname = sf.save(commit=False) - # Manually set any data: - surname.prefix = sf.cleaned_data["prefix"] if sf.cleaned_data["prefix"] != " prefix " else "" - surname.primary = True # FIXME: why is this False? Remove from form? - surname.save() - person.save_cache() - return redirect("/person/%s/name/%s%s#tab-surnames" % (person.handle, name.order, - build_search(request))) - else: - act = "edit" - context = RequestContext(request) - context["action"] = act - context["tview"] = _('Name') - context["tviews"] = _('Names') - context["view"] = 'name' - context["handle"] = handle - context["id"] = id - context["person"] = person - context["object"] = person - context["nameform"] = nf - context["surnameform"] = sf - context["order"] = order - context["next"] = "/person/%s/name/%d" % (person.handle, name.order) - view_template = "view_name_detail.html" - return render_to_response(view_template, context) - -def process_person(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Person") - context["tviews"] = _("People") - logform = None - if request.user.is_authenticated(): - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick a person", - Person, - ("name__surname__surname", - "name__first_name"), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to # ("Family", handle) - pickform = PickForm("Pick a person", - Person, - ("name__surname__surname", - "name__first_name"), - request.POST) - if pickform.data["picklist"]: - person_handle = pickform.data["picklist"] - person = Person.objects.get(handle=person_handle) - model = dji.get_model(item) # what model? - obj = model.objects.get(handle=handle) # get family - dji.add_child_ref_default(obj, person) # add person to family - #person.parent_families.add(obj) # add family to child - pfo = MyParentFamilies(person=person, family=obj, - order=len(person.parent_families.all())+1) - pfo.save() - person.save_cache() # rebuild child - obj.save_cache() # rebuild family - return redirect("/%s/%s%s" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = "family" - return render_to_response("pick.html", context) - elif act in ["edit", "view"]: - pf, nf, sf, person = get_person_forms(handle, empty=False) - if act == "edit": - logform = LogForm() - elif act == "add": - pf, nf, sf, person = get_person_forms(handle=None, protect=False, empty=True) - logform = LogForm() - elif act == "delete": - pf, nf, sf, person = get_person_forms(handle, protect=False, empty=True) - person.delete() - return redirect("/person/%s" % build_search(request)) - elif act in ["save", "create"]: # could be create a new person - # look up old data, if any: - logform = LogForm(request.POST) - if handle: - person = Person.objects.get(handle=handle) - name = person.name_set.get(preferred=True) - surname = name.surname_set.get(primary=True) - else: # create new item - person = Person(handle=create_id()) - name = Name(person=person, preferred=True) - surname = Surname(name=name, primary=True, order=1) - surname = Surname(name=name, - primary=True, - order=1, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0])) - # combine with user data: - pf = PersonForm(request.POST, instance=person) - pf.model = person - nf = NameFormFromPerson(request.POST, instance=name) - nf.model = name - sf = SurnameForm(request.POST, instance=surname) - sf.model = surname - # check if valid: - if nf.is_valid() and pf.is_valid() and sf.is_valid() and logform.is_valid(): - # name.preferred and surname.primary get set False in the above is_valid() - update_last_changed(person, request.user.username) - person = pf.save(save_cache=False) - # Process data: - name.person = person - name = nf.save(commit=False) - # Manually set any data: - name.suffix = nf.cleaned_data["suffix"] if nf.cleaned_data["suffix"] != " suffix " else "" - name.preferred = True # FIXME: why is this False? Remove from form? - check_preferred(request, name, person) - update_last_changed(name, request.user.username) - name.save() - # Process data: - surname.name = name - surname = sf.save(commit=False) - # Manually set any data: - surname.prefix = sf.cleaned_data["prefix"] if sf.cleaned_data["prefix"] != " prefix " else "" - surname.primary = True # FIXME: why is this False? Remove from form? - surname.save() - # FIXME: put this in correct place to get correct cache, before changes: - make_log(person, act, request.user.username, logform.cleaned_data["reason"], person.cache) - if add_to: # Adding a child to the family - item, handle = add_to # ("Family", handle) - model = dji.get_model(item) # what model? - obj = model.objects.get(handle=handle) # get family - dji.add_child_ref_default(obj, person) # add person to family - #person.parent_families.add(obj) # add family to child - pfo = MyParentFamilies(person=person, family=obj, - order=len(person.parent_families.all())+1) - pfo.save() - person.save_cache() # rebuild child - obj.save_cache() # rebuild family - return redirect("/%s/%s%s" % (item, handle, build_search(request))) - person.save_cache() - return redirect("/person/%s%s" % (person.handle, build_search(request))) - else: - # need to edit again - if handle: - act = "edit" - else: - act = "add" - else: # error? - raise Http404(_("Requested %s does not exist.") % "person") - else: # not authenticated - # BEGIN NON-AUTHENTICATED ACCESS - try: - person = Person.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % "person") - if person.private: - raise Http404(_("Requested %s does not exist.") % "person") - pf, nf, sf, person = get_person_forms(handle, protect=True) - # END NON-AUTHENTICATED ACCESS - context["action"] = act - context["view"] = "person" - context["tview"] = _("Person") - context["tviews"] = _("People") - context["personform"] = pf - context["nameform"] = nf - context["surnameform"] = sf - context["logform"] = logform - context["person"] = person - context["object"] = person - context["next"] = "/person/%s" % person.handle - -def get_person_forms(handle, protect=False, empty=False, order=None): - if handle: - person = Person.objects.get(handle=handle) - else: - person = Person(gramps_id=dji.get_next_id(Person, "I")) - ## get a name - name = None - if order is not None: - try: - name = person.name_set.get(order=order) - except: - pass - if name is None: - try: - name = person.name_set.get(preferred=True) - except: - name = Name(person=person, preferred=True, - display_as=NameFormatType.objects.get(val=NameFormatType._DEFAULT[0]), - sort_as=NameFormatType.objects.get(val=NameFormatType._DEFAULT[0]), - name_type=NameType.objects.get(val=NameType._DEFAULT[0])) - ## get a surname - try: - surname = name.surname_set.get(primary=True) - except: - surname = Surname(name=name, primary=True, - name_origin_type=NameOriginType.objects.get(val=NameOriginType._DEFAULT[0]), - order=1) - - if protect and person.probably_alive: - name.sanitize() - pf = PersonForm(instance=person) - pf.model = person - name.suffix = make_empty(empty, name.suffix, " suffix ") - nf = NameForm(instance=name) - nf.model = name - surname.prefix = make_empty(empty, surname.prefix, " prefix ") - sf = SurnameForm(instance=surname) - sf.model = surname - return pf, nf, sf, person - -def make_empty(empty, value, empty_value): - if value: - return value - elif empty: - return empty_value - else: - return value - diff --git a/gramps/webapp/grampsdb/view/place.py b/gramps/webapp/grampsdb/view/place.py deleted file mode 100644 index 70b43eb96..000000000 --- a/gramps/webapp/grampsdb/view/place.py +++ /dev/null @@ -1,97 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed -from gramps.webapp.grampsdb.models import Place -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_place(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Place") - context["tviews"] = _("Places") - context["action"] = "view" - view_template = "view_place_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete - if act == "add": - place = Place(gramps_id=dji.get_next_id(Place, "P")) - placeform = PlaceForm(instance=place) - placeform.model = place - elif act in ["view", "edit"]: - place = Place.objects.get(handle=handle) - placeform = PlaceForm(instance=place) - placeform.model = place - elif act == "save": - place = Place.objects.get(handle=handle) - placeform = PlaceForm(request.POST, instance=place) - placeform.model = place - if placeform.is_valid(): - update_last_changed(place, request.user.username) - place = placeform.save() - act = "view" - else: - act = "edit" - elif act == "create": - place = Place(handle=create_id()) - placeform = PlaceForm(request.POST, instance=place) - placeform.model = place - if placeform.is_valid(): - update_last_changed(place, request.user.username) - place = placeform.save() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_place_ref(obj, place.handle) - obj.save_cache() - return redirect("/%s/%s#tab-places" % (item, handle)) - act = "view" - else: - act = "add" - elif act == "delete": - place = Place.objects.get(handle=handle) - place.delete() - return redirect("/place/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["placeform"] = placeform - context["object"] = place - context["place"] = place - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/view/png.py b/gramps/webapp/grampsdb/view/png.py deleted file mode 100644 index 434de5779..000000000 --- a/gramps/webapp/grampsdb/view/png.py +++ /dev/null @@ -1,2199 +0,0 @@ -#!/usr/bin/env python - -# $URL$ -# $Rev$ - -# png.py - PNG encoder/decoder in pure Python -# -# Copyright (C) 2006 Johann C. Rocholl -# Portions Copyright (C) 2009 David Jones -# And probably portions Copyright (C) 2006 Nicko van Someren -# -# Original concept by Johann C. Rocholl. -# -# LICENSE (The MIT License) -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Changelog (recent first): -# 2009-03-11 David: interlaced bit depth < 8 (writing). -# 2009-03-10 David: interlaced bit depth < 8 (reading). -# 2009-03-04 David: Flat and Boxed pixel formats. -# 2009-02-26 David: Palette support (writing). -# 2009-02-23 David: Bit-depths < 8; better PNM support. -# 2006-06-17 Nicko: Reworked into a class, faster interlacing. -# 2006-06-17 Johann: Very simple prototype PNG decoder. -# 2006-06-17 Nicko: Test suite with various image generators. -# 2006-06-17 Nicko: Alpha-channel, grey-scale, 16-bit/plane support. -# 2006-06-15 Johann: Scanline iterator interface for large input files. -# 2006-06-09 Johann: Very simple prototype PNG encoder. - -# Incorporated into Bangai-O Development Tools by drj on 2009-02-11 from -# http://trac.browsershots.org/browser/trunk/pypng/lib/png.py?rev=2885 - -# Incorporated into pypng by drj on 2009-03-12 from -# //depot/prj/bangaio/master/code/png.py#67 - - -""" -Pure Python PNG Reader/Writer - -This Python module implements support for PNG images (see PNG -specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads -and writes PNG files with all allowable bit depths (1/2/4/8/16/24/32/48/64 -bits per pixel) and colour combinations: greyscale (1/2/4/8/16 bit); RGB, -RGBA, LA (greyscale with alpha) with 8/16 bits per channel; colour mapped -images (1/2/4/8 bit). Adam7 interlacing is supported for reading and -writing. A number of optional chunks can be specified (when writing) -and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``. - -For help, type ``import png; help(png)`` in your python interpreter. - -A good place to start is the :class:`Reader` and :class:`Writer` classes. - -Requires Python 2.3. Limited support is available for Python 2.2, but -not everything works. Best with Python 2.4 and higher. Installation is -trivial, but see the ``README.txt`` file (with the source distribution) -for details. - -This file can also be used as a command-line utility to convert -`Netpbm `_ PNM files to PNG, and the reverse conversion from PNG to -PNM. The interface is similar to that of the ``pnmtopng`` program from -Netpbm. Type ``python png.py --help`` at the shell prompt -for usage and a list of options. - -A note on spelling and terminology ----------------------------------- - -Generally British English spelling is used in the documentation. So -that's "greyscale" and "colour". This not only matches the author's -native language, it's also used by the PNG specification. - -The major colour models supported by PNG (and hence by PyPNG) are: -greyscale, RGB, greyscale--alpha, RGB--alpha. These are sometimes -referred to using the abbreviations: L, RGB, LA, RGBA. In this case -each letter abbreviates a single channel: *L* is for Luminance or Luma or -Lightness which is the channel used in greyscale images; *R*, *G*, *B* stand -for Red, Green, Blue, the components of a colour image; *A* stands for -Alpha, the opacity channel (used for transparency effects, but higher -values are more opaque, so it makes sense to call it opacity). - -A note on formats ------------------ - -When getting pixel data out of this module (reading) and presenting -data to this module (writing) there are a number of ways the data could -be represented as a Python value. Generally this module uses one of -three formats called "flat row flat pixel", "boxed row flat pixel", and -"boxed row boxed pixel". Basically the concern is whether each pixel -and each row comes in its own little tuple (box), or not. - -Consider an image that is 3 pixels wide by 2 pixels high, and each pixel -has RGB components: - -Boxed row flat pixel:: - - list([R,G,B, R,G,B, R,G,B], - [R,G,B, R,G,B, R,G,B]) - -Each row appears as its own list, but the pixels are flattened so that -three values for one pixel simply follow the three values for the previous -pixel. This is the most common format used, because it provides a good -compromise between space and convenience. PyPNG regards itself as -at liberty to replace any sequence type with any sufficiently compatible -other sequence type; in practice each row is an array (from the array -module), and the outer list is sometimes an iterator rather than an -explicit list (so that streaming is possible). - -Flat row flat pixel:: - - [R,G,B, R,G,B, R,G,B, - R,G,B, R,G,B, R,G,B] - -The entire image is one single giant sequence of colour values. -Generally an array will be used (to save space), not a list. - -Boxed row boxed pixel:: - - list([ (R,G,B), (R,G,B), (R,G,B) ], - [ (R,G,B), (R,G,B), (R,G,B) ]) - -Each row appears in its own list, but each pixel also appears in its own -tuple. A serious memory burn in Python. - -In all cases the top row comes first, and for each row the pixels are -ordered from left-to-right. Within a pixel the values appear in the -order, R-G-B-A (or L-A for greyscale--alpha). - -There is a fourth format, mentioned because it is used internally, -is close to what lies inside a PNG file itself, and has some support -from the public API. This format is called packed. When packed, -each row is a sequence of bytes (integers from 0 to 255), just as -it is before PNG scanline filtering is applied. When the bit depth -is 8 this is essentially the same as boxed row flat pixel; when the -bit depth is less than 8, several pixels are packed into each byte; -when the bit depth is 16 (the only value more than 8 that is supported -by the PNG image format) each pixel value is decomposed into 2 bytes -(and `packed` is a misnomer). This format is used by the -:meth:`Writer.write_packed` method. It isn't usually a convenient -format, but may be just right if the source data for the PNG image -comes from something that uses a similar format (for example, 1-bit -BMPs, or another PNG file). - -And now, my famous members --------------------------- -""" - -# http://www.python.org/doc/2.2.3/whatsnew/node5.html -__version__ = "$URL$ $Rev$" - -from array import array -from functools import reduce -try: # See :pyver:old - import itertools -except: - pass -import math -# http://www.python.org/doc/2.4.4/lib/module-operator.html -import operator -import struct -import sys -import zlib -# http://www.python.org/doc/2.4.4/lib/module-warnings.html -import warnings - - -__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array'] - - -# The PNG signature. -# http://www.w3.org/TR/PNG/#5PNG-file-signature -_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10) - -_adam7 = ((0, 0, 8, 8), - (4, 0, 8, 8), - (0, 4, 4, 8), - (2, 0, 4, 4), - (0, 2, 2, 4), - (1, 0, 2, 2), - (0, 1, 1, 2)) - -def group(s, n): - # See - # http://www.python.org/doc/2.6/library/functions.html#zip - return list(zip(*[iter(s)]*n)) - -def isarray(x): - """Same as ``isinstance(x, array)`` except on Python 2.2, where it - always returns ``False``. This helps PyPNG work on Python 2.2. - """ - - try: - return isinstance(x, array) - except: - return False - -try: # see :pyver:old - array.tostring -except: - def tostring(row): - l = len(row) - return struct.pack('%dB' % l, *row) -else: - def tostring(row): - """Convert row of bytes to string. Expects `row` to be an - ``array``. - """ - return row.tostring() - -# Conditionally convert to bytes. Works on Python 2 and Python 3. -try: - bytes('', 'ascii') - def strtobytes(x): return bytes(x, 'iso8859-1') - def bytestostr(x): return str(x, 'iso8859-1') -except: - strtobytes = str - bytestostr = str - -def interleave_planes(ipixels, apixels, ipsize, apsize): - """ - Interleave (colour) planes, e.g. RGB + A = RGBA. - - Return an array of pixels consisting of the `ipsize` elements of data - from each pixel in `ipixels` followed by the `apsize` elements of data - from each pixel in `apixels`. Conventionally `ipixels` and - `apixels` are byte arrays so the sizes are bytes, but it actually - works with any arrays of the same type. The returned array is the - same type as the input arrays which should be the same type as each other. - """ - - itotal = len(ipixels) - atotal = len(apixels) - newtotal = itotal + atotal - newpsize = ipsize + apsize - # Set up the output buffer - # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356 - out = array(ipixels.typecode) - # It's annoying that there is no cheap way to set the array size :-( - out.extend(ipixels) - out.extend(apixels) - # Interleave in the pixel data - for i in range(ipsize): - out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize] - for i in range(apsize): - out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize] - return out - -def check_palette(palette): - """Check a palette argument (to the :class:`Writer` class) for validity. - Returns the palette as a list if okay; raises an exception otherwise. - """ - - # None is the default and is allowed. - if palette is None: - return None - - p = list(palette) - if not (0 < len(p) <= 256): - raise ValueError("a palette must have between 1 and 256 entries") - seen_triple = False - for i,t in enumerate(p): - if len(t) not in (3,4): - raise ValueError( - "palette entry %d: entries must be 3- or 4-tuples." % i) - if len(t) == 3: - seen_triple = True - if seen_triple and len(t) == 4: - raise ValueError( - "palette entry %d: all 4-tuples must precede all 3-tuples" % i) - for x in t: - if int(x) != x or not(0 <= x <= 255): - raise ValueError( - "palette entry %d: values must be integer: 0 <= x <= 255" % i) - return p - -class Error(Exception): - prefix = 'Error' - def __str__(self): - return self.prefix + ': ' + ' '.join(self.args) - -class FormatError(Error): - """Problem with input file format. In other words, PNG file does - not conform to the specification in some way and is invalid. - """ - - prefix = 'FormatError' - -class ChunkError(FormatError): - prefix = 'ChunkError' - - -class Writer: - """ - PNG encoder in pure Python. - """ - - def __init__(self, width=None, height=None, - size=None, - greyscale=False, - alpha=False, - bitdepth=8, - palette=None, - transparent=None, - background=None, - gamma=None, - compression=None, - interlace=False, - bytes_per_sample=None, # deprecated - planes=None, - colormap=None, - maxval=None, - chunk_limit=2**20): - """ - Create a PNG encoder object. - - Arguments: - - width, height - Image size in pixels, as two separate arguments. - size - Image size (w,h) in pixels, as single argument. - greyscale - Input data is greyscale, not RGB. - alpha - Input data has alpha channel (RGBA or LA). - bitdepth - Bit depth: from 1 to 16. - palette - Create a palette for a colour mapped image (colour type 3). - transparent - Specify a transparent colour (create a ``tRNS`` chunk). - background - Specify a default background colour (create a ``bKGD`` chunk). - gamma - Specify a gamma value (create a ``gAMA`` chunk). - compression - zlib compression level (1-9). - interlace - Create an interlaced image. - chunk_limit - Write multiple ``IDAT`` chunks to save memory. - - The image size (in pixels) can be specified either by using the - `width` and `height` arguments, or with the single `size` - argument. If `size` is used it should be a pair (*width*, - *height*). - - `greyscale` and `alpha` are booleans that specify whether - an image is greyscale (or colour), and whether it has an - alpha channel (or not). - - `bitdepth` specifies the bit depth of the source pixel values. - Each source pixel value must be an integer between 0 and - ``2**bitdepth-1``. For example, 8-bit images have values - between 0 and 255. PNG only stores images with bit depths of - 1,2,4,8, or 16. When `bitdepth` is not one of these values, - the next highest valid bit depth is selected, and an ``sBIT`` - (significant bits) chunk is generated that specifies the original - precision of the source image. In this case the supplied pixel - values will be rescaled to fit the range of the selected bit depth. - - The details of which bit depth / colour model combinations the - PNG file format supports directly, are somewhat arcane - (refer to the PNG specification for full details). Briefly: - "small" bit depths (1,2,4) are only allowed with greyscale and - colour mapped images; colour mapped images cannot have bit depth - 16. - - For colour mapped images (in other words, when the `palette` - argument is specified) the `bitdepth` argument must match one of - the valid PNG bit depths: 1, 2, 4, or 8. (It is valid to have a - PNG image with a palette and an ``sBIT`` chunk, but the meaning - is slightly different; it would be awkward to press the - `bitdepth` argument into service for this.) - - The `palette` option, when specified, causes a colour mapped image - to be created: the PNG colour type is set to 3; greyscale - must not be set; alpha must not be set; transparent must - not be set; the bit depth must be 1,2,4, or 8. When a colour - mapped image is created, the pixel values are palette indexes - and the `bitdepth` argument specifies the size of these indexes - (not the size of the colour values in the palette). - - The palette argument value should be a sequence of 3- or - 4-tuples. 3-tuples specify RGB palette entries; 4-tuples - specify RGBA palette entries. If both 4-tuples and 3-tuples - appear in the sequence then all the 4-tuples must come - before all the 3-tuples. A ``PLTE`` chunk is created; if there - are 4-tuples then a ``tRNS`` chunk is created as well. The - ``PLTE`` chunk will contain all the RGB triples in the same - sequence; the ``tRNS`` chunk will contain the alpha channel for - all the 4-tuples, in the same sequence. Palette entries - are always 8-bit. - - If specified, the `transparent` and `background` parameters must - be a tuple with three integer values for red, green, blue, or - a simple integer (or singleton tuple) for a greyscale image. - - If specified, the `gamma` parameter must be a positive number - (generally, a float). A ``gAMA`` chunk will be created. Note that - this will not change the values of the pixels as they appear in - the PNG file, they are assumed to have already been converted - appropriately for the gamma specified. - - The `compression` argument specifies the compression level - to be used by the ``zlib`` module. Higher values are likely - to compress better, but will be slower to compress. The - default for this argument is ``None``; this does not mean - no compression, rather it means that the default from the - ``zlib`` module is used (which is generally acceptable). - - If `interlace` is true then an interlaced image is created - (using PNG's so far only interace method, *Adam7*). This does not - affect how the pixels should be presented to the encoder, rather - it changes how they are arranged into the PNG file. On slow - connexions interlaced images can be partially decoded by the - browser to give a rough view of the image that is successively - refined as more image data appears. - - .. note :: - - Enabling the `interlace` option requires the entire image - to be processed in working memory. - - `chunk_limit` is used to limit the amount of memory used whilst - compressing the image. In order to avoid using large amounts of - memory, multiple ``IDAT`` chunks may be created. - """ - - # At the moment the `planes` argument is ignored; - # its purpose is to act as a dummy so that - # ``Writer(x, y, **info)`` works, where `info` is a dictionary - # returned by Reader.read and friends. - # Ditto for `colormap`. - - # A couple of helper functions come first. Best skipped if you - # are reading through. - - def isinteger(x): - try: - return int(x) == x - except: - return False - - def check_color(c, which): - """Checks that a colour argument for transparent or - background options is the right form. Also "corrects" bare - integers to 1-tuples. - """ - - if c is None: - return c - if greyscale: - try: - l = len(c) - except TypeError: - c = (c,) - if len(c) != 1: - raise ValueError("%s for greyscale must be 1-tuple" % - which) - if not isinteger(c[0]): - raise ValueError( - "%s colour for greyscale must be integer" % - which) - else: - if not (len(c) == 3 and - isinteger(c[0]) and - isinteger(c[1]) and - isinteger(c[2])): - raise ValueError( - "%s colour must be a triple of integers" % - which) - return c - - if size: - if len(size) != 2: - raise ValueError( - "size argument should be a pair (width, height)") - if width is not None and width != size[0]: - raise ValueError( - "size[0] (%r) and width (%r) should match when both are used." - % (size[0], width)) - if height is not None and height != size[1]: - raise ValueError( - "size[1] (%r) and height (%r) should match when both are used." - % (size[1], height)) - width,height = size - del size - - if width <= 0 or height <= 0: - raise ValueError("width and height must be greater than zero") - if not isinteger(width) or not isinteger(height): - raise ValueError("width and height must be integers") - # http://www.w3.org/TR/PNG/#7Integers-and-byte-order - if width > 2**32-1 or height > 2**32-1: - raise ValueError("width and height cannot exceed 2**32-1") - - if alpha and transparent is not None: - raise ValueError( - "transparent colour not allowed with alpha channel") - - if bytes_per_sample is not None: - warnings.warn('please use bitdepth instead of bytes_per_sample', - DeprecationWarning) - if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2): - raise ValueError( - "bytes per sample must be .125, .25, .5, 1, or 2") - bitdepth = int(8*bytes_per_sample) - del bytes_per_sample - if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth: - raise ValueError("bitdepth (%r) must be a postive integer <= 16" % - bitdepth) - - self.rescale = None - if palette: - if bitdepth not in (1,2,4,8): - raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8") - if transparent is not None: - raise ValueError("transparent and palette not compatible") - if alpha: - raise ValueError("alpha and palette not compatible") - if greyscale: - raise ValueError("greyscale and palette not compatible") - else: - # No palette, check for sBIT chunk generation. - if alpha or not greyscale: - if bitdepth not in (8,16): - targetbitdepth = (8,16)[bitdepth > 8] - self.rescale = (bitdepth, targetbitdepth) - bitdepth = targetbitdepth - del targetbitdepth - else: - assert greyscale - assert not alpha - if bitdepth not in (1,2,4,8,16): - if bitdepth > 8: - targetbitdepth = 16 - elif bitdepth == 3: - targetbitdepth = 4 - else: - assert bitdepth in (5,6,7) - targetbitdepth = 8 - self.rescale = (bitdepth, targetbitdepth) - bitdepth = targetbitdepth - del targetbitdepth - - if bitdepth < 8 and (alpha or not greyscale and not palette): - raise ValueError( - "bitdepth < 8 only permitted with greyscale or palette") - if bitdepth > 8 and palette: - raise ValueError( - "bit depth must be 8 or less for images with palette") - - transparent = check_color(transparent, 'transparent') - background = check_color(background, 'background') - - # It's important that the true boolean values (greyscale, alpha, - # colormap, interlace) are converted to bool because Iverson's - # convention is relied upon later on. - self.width = width - self.height = height - self.transparent = transparent - self.background = background - self.gamma = gamma - self.greyscale = bool(greyscale) - self.alpha = bool(alpha) - self.colormap = bool(palette) - self.bitdepth = int(bitdepth) - self.compression = compression - self.chunk_limit = chunk_limit - self.interlace = bool(interlace) - self.palette = check_palette(palette) - - self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap - assert self.color_type in (0,2,3,4,6) - - self.color_planes = (3,1)[self.greyscale or self.colormap] - self.planes = self.color_planes + self.alpha - # :todo: fix for bitdepth < 8 - self.psize = (self.bitdepth/8) * self.planes - - def make_palette(self): - """Create the byte sequences for a ``PLTE`` and if necessary a - ``tRNS`` chunk. Returned as a pair (*p*, *t*). *t* will be - ``None`` if no ``tRNS`` chunk is necessary. - """ - - p = array('B') - t = array('B') - - for x in self.palette: - p.extend(x[0:3]) - if len(x) > 3: - t.append(x[3]) - p = tostring(p) - t = tostring(t) - if t: - return p,t - return p,None - - def write(self, outfile, rows): - """Write a PNG image to the output file. `rows` should be - an iterable that yields each row in boxed row flat pixel format. - The rows should be the rows of the original image, so there - should be ``self.height`` rows of ``self.width * self.planes`` values. - If `interlace` is specified (when creating the instance), then - an interlaced PNG file will be written. Supply the rows in the - normal image order; the interlacing is carried out internally. - - .. note :: - - Interlacing will require the entire image to be in working memory. - """ - - if self.interlace: - fmt = 'BH'[self.bitdepth > 8] - a = array(fmt, itertools.chain(*rows)) - return self.write_array(outfile, a) - else: - nrows = self.write_passes(outfile, rows) - if nrows != self.height: - raise ValueError( - "rows supplied (%d) does not match height (%d)" % - (nrows, self.height)) - - def write_passes(self, outfile, rows, packed=False): - """ - Write a PNG image to the output file. - - Most users are expected to find the :meth:`write` or - :meth:`write_array` method more convenient. - - The rows should be given to this method in the order that - they appear in the output file. For straightlaced images, - this is the usual top to bottom ordering, but for interlaced - images the rows should have already been interlaced before - passing them to this function. - - `rows` should be an iterable that yields each row. When - `packed` is ``False`` the rows should be in boxed row flat pixel - format; when `packed` is ``True`` each row should be a packed - sequence of bytes. - - """ - - # http://www.w3.org/TR/PNG/#5PNG-file-signature - outfile.write(_signature) - - # http://www.w3.org/TR/PNG/#11IHDR - write_chunk(outfile, 'IHDR', - struct.pack("!2I5B", self.width, self.height, - self.bitdepth, self.color_type, - 0, 0, self.interlace)) - - # See :chunk:order - # http://www.w3.org/TR/PNG/#11gAMA - if self.gamma is not None: - write_chunk(outfile, 'gAMA', - struct.pack("!L", int(round(self.gamma*1e5)))) - - # See :chunk:order - # http://www.w3.org/TR/PNG/#11sBIT - if self.rescale: - write_chunk(outfile, 'sBIT', - struct.pack('%dB' % self.planes, - *[self.rescale[0]]*self.planes)) - - # :chunk:order: Without a palette (PLTE chunk), ordering is - # relatively relaxed. With one, gAMA chunk must precede PLTE - # chunk which must precede tRNS and bKGD. - # See http://www.w3.org/TR/PNG/#5ChunkOrdering - if self.palette: - p,t = self.make_palette() - write_chunk(outfile, 'PLTE', p) - if t: - # tRNS chunk is optional. Only needed if palette entries - # have alpha. - write_chunk(outfile, 'tRNS', t) - - # http://www.w3.org/TR/PNG/#11tRNS - if self.transparent is not None: - if self.greyscale: - write_chunk(outfile, 'tRNS', - struct.pack("!1H", *self.transparent)) - else: - write_chunk(outfile, 'tRNS', - struct.pack("!3H", *self.transparent)) - - # http://www.w3.org/TR/PNG/#11bKGD - if self.background is not None: - if self.greyscale: - write_chunk(outfile, 'bKGD', - struct.pack("!1H", *self.background)) - else: - write_chunk(outfile, 'bKGD', - struct.pack("!3H", *self.background)) - - # http://www.w3.org/TR/PNG/#11IDAT - if self.compression is not None: - compressor = zlib.compressobj(self.compression) - else: - compressor = zlib.compressobj() - - # Choose an extend function based on the bitdepth. The extend - # function packs/decomposes the pixel values into bytes and - # stuffs them onto the data array. - data = array('B') - if self.bitdepth == 8 or packed: - extend = data.extend - elif self.bitdepth == 16: - # Decompose into bytes - def extend(sl): - fmt = '!%dH' % len(sl) - data.extend(array('B', struct.pack(fmt, *sl))) - else: - # Pack into bytes - assert self.bitdepth < 8 - # samples per byte - spb = int(8/self.bitdepth) - def extend(sl): - a = array('B', sl) - # Adding padding bytes so we can group into a whole - # number of spb-tuples. - l = float(len(a)) - extra = math.ceil(l / float(spb))*spb - l - a.extend([0]*int(extra)) - # Pack into bytes - l = group(a, spb) - l = [reduce(lambda x,y: - (x << self.bitdepth) + y, e) for e in l] - data.extend(l) - if self.rescale: - oldextend = extend - factor = \ - float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1) - def extend(sl): - oldextend([int(round(factor*x)) for x in sl]) - - # Build the first row, testing mostly to see if we need to - # changed the extend function to cope with NumPy integer types - # (they cause our ordinary definition of extend to fail, so we - # wrap it). See - # http://code.google.com/p/pypng/issues/detail?id=44 - enumrows = enumerate(rows) - del rows - - # First row's filter type. - data.append(0) - # :todo: Certain exceptions in the call to ``.next()`` or the - # following try would indicate no row data supplied. - # Should catch. - i,row = next(enumrows) - try: - # If this fails... - extend(row) - except: - # ... try a version that converts the values to int first. - # Not only does this work for the (slightly broken) NumPy - # types, there are probably lots of other, unknown, "nearly" - # int types it works for. - def wrapmapint(f): - return lambda sl: f(list(map(int, sl))) - extend = wrapmapint(extend) - del wrapmapint - extend(row) - - for i,row in enumrows: - # Add "None" filter type. Currently, it's essential that - # this filter type be used for every scanline as we do not - # mark the first row of a reduced pass image; that means we - # could accidentally compute the wrong filtered scanline if - # we used "up", "average", or "paeth" on such a line. - data.append(0) - extend(row) - if len(data) > self.chunk_limit: - compressed = compressor.compress(tostring(data)) - if len(compressed): - # print >> sys.stderr, len(data), len(compressed) - write_chunk(outfile, 'IDAT', compressed) - # Because of our very witty definition of ``extend``, - # above, we must re-use the same ``data`` object. Hence - # we use ``del`` to empty this one, rather than create a - # fresh one (which would be my natural FP instinct). - del data[:] - if len(data): - compressed = compressor.compress(tostring(data)) - else: - compressed = '' - flushed = compressor.flush() - if len(compressed) or len(flushed): - # print >> sys.stderr, len(data), len(compressed), len(flushed) - write_chunk(outfile, 'IDAT', compressed + flushed) - # http://www.w3.org/TR/PNG/#11IEND - write_chunk(outfile, 'IEND') - return i+1 - - def write_array(self, outfile, pixels): - """ - Write an array in flat row flat pixel format as a PNG file on - the output file. See also :meth:`write` method. - """ - - if self.interlace: - self.write_passes(outfile, self.array_scanlines_interlace(pixels)) - else: - self.write_passes(outfile, self.array_scanlines(pixels)) - - def write_packed(self, outfile, rows): - """ - Write PNG file to `outfile`. The pixel data comes from `rows` - which should be in boxed row packed format. Each row should be - a sequence of packed bytes. - - Technically, this method does work for interlaced images but it - is best avoided. For interlaced images, the rows should be - presented in the order that they appear in the file. - - This method should not be used when the source image bit depth - is not one naturally supported by PNG; the bit depth should be - 1, 2, 4, 8, or 16. - """ - - if self.rescale: - raise Error("write_packed method not suitable for bit depth %d" % - self.rescale[0]) - return self.write_passes(outfile, rows, packed=True) - - def convert_pnm(self, infile, outfile): - """ - Convert a PNM file containing raw pixel data into a PNG file - with the parameters set in the writer object. Works for - (binary) PGM, PPM, and PAM formats. - """ - - if self.interlace: - pixels = array('B') - pixels.fromfile(infile, - (self.bitdepth/8) * self.color_planes * - self.width * self.height) - self.write_passes(outfile, self.array_scanlines_interlace(pixels)) - else: - self.write_passes(outfile, self.file_scanlines(infile)) - - def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile): - """ - Convert a PPM and PGM file containing raw pixel data into a - PNG outfile with the parameters set in the writer object. - """ - pixels = array('B') - pixels.fromfile(ppmfile, - (self.bitdepth/8) * self.color_planes * - self.width * self.height) - apixels = array('B') - apixels.fromfile(pgmfile, - (self.bitdepth/8) * - self.width * self.height) - pixels = interleave_planes(pixels, apixels, - (self.bitdepth/8) * self.color_planes, - (self.bitdepth/8)) - if self.interlace: - self.write_passes(outfile, self.array_scanlines_interlace(pixels)) - else: - self.write_passes(outfile, self.array_scanlines(pixels)) - - def file_scanlines(self, infile): - """ - Generates boxed rows in flat pixel format, from the input file - `infile`. It assumes that the input file is in a "Netpbm-like" - binary format, and is positioned at the beginning of the first - pixel. The number of pixels to read is taken from the image - dimensions (`width`, `height`, `planes`) and the number of bytes - per value is implied by the image `bitdepth`. - """ - - # Values per row - vpr = self.width * self.planes - row_bytes = vpr - if self.bitdepth > 8: - assert self.bitdepth == 16 - row_bytes *= 2 - fmt = '>%dH' % vpr - def line(): - return array('H', struct.unpack(fmt, infile.read(row_bytes))) - else: - def line(): - scanline = array('B', infile.read(row_bytes)) - return scanline - for y in range(self.height): - yield line() - - def array_scanlines(self, pixels): - """ - Generates boxed rows (flat pixels) from flat rows (flat pixels) - in an array. - """ - - # Values per row - vpr = self.width * self.planes - stop = 0 - for y in range(self.height): - start = stop - stop = start + vpr - yield pixels[start:stop] - - def array_scanlines_interlace(self, pixels): - """ - Generator for interlaced scanlines from an array. `pixels` is - the full source image in flat row flat pixel format. The - generator yields each scanline of the reduced passes in turn, in - boxed row flat pixel format. - """ - - # http://www.w3.org/TR/PNG/#8InterlaceMethods - # Array type. - fmt = 'BH'[self.bitdepth > 8] - # Value per row - vpr = self.width * self.planes - for xstart, ystart, xstep, ystep in _adam7: - if xstart >= self.width: - continue - # Pixels per row (of reduced image) - ppr = int(math.ceil((self.width-xstart)/float(xstep))) - # number of values in reduced image row. - row_len = ppr*self.planes - for y in range(ystart, self.height, ystep): - if xstep == 1: - offset = y * vpr - yield pixels[offset:offset+vpr] - else: - row = array(fmt) - # There's no easier way to set the length of an array - row.extend(pixels[0:row_len]) - offset = y * vpr + xstart * self.planes - end_offset = (y+1) * vpr - skip = self.planes * xstep - for i in range(self.planes): - row[i::self.planes] = \ - pixels[offset+i:end_offset:skip] - yield row - -def write_chunk(outfile, tag, data=strtobytes('')): - """ - Write a PNG chunk to the output file, including length and - checksum. - """ - - # http://www.w3.org/TR/PNG/#5Chunk-layout - outfile.write(struct.pack("!I", len(data))) - tag = strtobytes(tag) - outfile.write(tag) - outfile.write(data) - checksum = zlib.crc32(tag) - checksum = zlib.crc32(data, checksum) - checksum &= 2**32-1 - outfile.write(struct.pack("!I", checksum)) - -def write_chunks(out, chunks): - """Create a PNG file by writing out the chunks.""" - - out.write(_signature) - for chunk in chunks: - write_chunk(out, *chunk) - -def filter_scanline(type, line, fo, prev=None): - """Apply a scanline filter to a scanline. `type` specifies the - filter type (0 to 4); `line` specifies the current (unfiltered) - scanline as a sequence of bytes; `prev` specifies the previous - (unfiltered) scanline as a sequence of bytes. `fo` specifies the - filter offset; normally this is size of a pixel in bytes (the number - of bytes per sample times the number of channels), but when this is - < 1 (for bit depths < 8) then the filter offset is 1. - """ - - assert 0 <= type < 5 - - # The output array. Which, pathetically, we extend one-byte at a - # time (fortunately this is linear). - out = array('B', [type]) - - def sub(): - ai = -fo - for x in line: - if ai >= 0: - x = (x - line[ai]) & 0xff - out.append(x) - ai += 1 - def up(): - for i,x in enumerate(line): - x = (x - prev[i]) & 0xff - out.append(x) - def average(): - ai = -fo - for i,x in enumerate(line): - if ai >= 0: - x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff - else: - x = (x - (prev[i] >> 1)) & 0xff - out.append(x) - ai += 1 - def paeth(): - # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth - ai = -fo # also used for ci - for i,x in enumerate(line): - a = 0 - b = prev[i] - c = 0 - - if ai >= 0: - a = line[ai] - c = prev[ai] - p = a + b - c - pa = abs(p - a) - pb = abs(p - b) - pc = abs(p - c) - if pa <= pb and pa <= pc: Pr = a - elif pb <= pc: Pr = b - else: Pr = c - - x = (x - Pr) & 0xff - out.append(x) - ai += 1 - - if not prev: - # We're on the first line. Some of the filters can be reduced - # to simpler cases which makes handling the line "off the top" - # of the image simpler. "up" becomes "none"; "paeth" becomes - # "left" (non-trivial, but true). "average" needs to be handled - # specially. - if type == 2: # "up" - return line # type = 0 - elif type == 3: - prev = [0]*len(line) - elif type == 4: # "paeth" - type = 1 - if type == 0: - out.extend(line) - elif type == 1: - sub() - elif type == 2: - up() - elif type == 3: - average() - else: # type == 4 - paeth() - return out - - -def from_array(a, mode=None, info={}): - """Create a PNG :class:`Image` object from a 2- or 3-dimensional array. - One application of this function is easy PIL-style saving: - ``png.from_array(pixels, 'L').save('foo.png')``. - - .. note : - - The use of the term *3-dimensional* is for marketing purposes - only. It doesn't actually work. Please bear with us. Meanwhile - enjoy the complimentary snacks (on request) and please use a - 2-dimensional array. - - Unless they are specified using the *info* parameter, the PNG's - height and width are taken from the array size. For a 3 dimensional - array the first axis is the height; the second axis is the width; - and the third axis is the channel number. Thus an RGB image that is - 16 pixels high and 8 wide will use an array that is 16x8x3. For 2 - dimensional arrays the first axis is the height, but the second axis - is ``width*channels``, so an RGB image that is 16 pixels high and 8 - wide will use a 2-dimensional array that is 16x24 (each row will be - 8*3==24 sample values). - - *mode* is a string that specifies the image colour format in a - PIL-style mode. It can be: - - ``'L'`` - greyscale (1 channel) - ``'LA'`` - greyscale with alpha (2 channel) - ``'RGB'`` - colour image (3 channel) - ``'RGBA'`` - colour image with alpha (4 channel) - - The mode string can also specify the bit depth (overriding how this - function normally derives the bit depth, see below). Appending - ``';16'`` to the mode will cause the PNG to be 16 bits per channel; - any decimal from 1 to 16 can be used to specify the bit depth. - - When a 2-dimensional array is used *mode* determines how many - channels the image has, and so allows the width to be derived from - the second array dimension. - - The array is expected to be a ``numpy`` array, but it can be any - suitable Python sequence. For example, a list of lists can be used: - ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``. The exact - rules are: ``len(a)`` gives the first dimension, height; - ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the - third dimension, unless an exception is raised in which case a - 2-dimensional array is assumed. It's slightly more complicated than - that because an iterator of rows can be used, and it all still - works. Using an iterator allows data to be streamed efficiently. - - The bit depth of the PNG is normally taken from the array element's - datatype (but if *mode* specifies a bitdepth then that is used - instead). The array element's datatype is determined in a way which - is supposed to work both for ``numpy`` arrays and for Python - ``array.array`` objects. A 1 byte datatype will give a bit depth of - 8, a 2 byte datatype will give a bit depth of 16. If the datatype - does not have an implicit size, for example it is a plain Python - list of lists, as above, then a default of 8 is used. - - The *info* parameter is a dictionary that can be used to specify - metadata (in the same style as the arguments to the - :class:``png.Writer`` class). For this function the keys that are - useful are: - - height - overrides the height derived from the array dimensions and allows - *a* to be an iterable. - width - overrides the width derived from the array dimensions. - bitdepth - overrides the bit depth derived from the element datatype (but - must match *mode* if that also specifies a bit depth). - - Generally anything specified in the - *info* dictionary will override any implicit choices that this - function would otherwise make, but must match any explicit ones. - For example, if the *info* dictionary has a ``greyscale`` key then - this must be true when mode is ``'L'`` or ``'LA'`` and false when - mode is ``'RGB'`` or ``'RGBA'``. - """ - - # We abuse the *info* parameter by modifying it. Take a copy here. - # (Also typechecks *info* to some extent). - info = dict(info) - - # Syntax check mode string. - bitdepth = None - try: - mode = mode.split(';') - if len(mode) not in (1,2): - raise Error() - if mode[0] not in ('L', 'LA', 'RGB', 'RGBA'): - raise Error() - if len(mode) == 2: - try: - bitdepth = int(mode[1]) - except: - raise Error() - except Error: - raise Error("mode string should be 'RGB' or 'L;16' or similar.") - mode = mode[0] - - # Get bitdepth from *mode* if possible. - if bitdepth: - if info.get('bitdepth') and bitdepth != info['bitdepth']: - raise Error("mode bitdepth (%d) should match info bitdepth (%d)." % - (bitdepth, info['bitdepth'])) - info['bitdepth'] = bitdepth - - # Fill in and/or check entries in *info*. - # Dimensions. - if 'size' in info: - # Check width, height, size all match where used. - for dimension,axis in [('width', 0), ('height', 1)]: - if dimension in info: - if info[dimension] != info['size'][axis]: - raise Error( - "info[%r] shhould match info['size'][%r]." % - (dimension, axis)) - info['width'],info['height'] = info['size'] - if 'height' not in info: - try: - l = len(a) - except: - raise Error( - "len(a) does not work, supply info['height'] instead.") - info['height'] = l - # Colour format. - if 'greyscale' in info: - if bool(info['greyscale']) != ('L' in mode): - raise Error("info['greyscale'] should match mode.") - info['greyscale'] = 'L' in mode - if 'alpha' in info: - if bool(info['alpha']) != ('A' in mode): - raise Error("info['alpha'] should match mode.") - info['alpha'] = 'A' in mode - - planes = len(mode) - if 'planes' in info: - if info['planes'] != planes: - raise Error("info['planes'] should match mode.") - - # In order to work out whether we the array is 2D or 3D we need its - # first row, which requires that we take a copy of its iterator. - # We may also need the first row to derive width and bitdepth. - a,t = itertools.tee(a) - row = next(t) - del t - try: - row[0][0] - threed = True - testelement = row[0] - except: - threed = False - testelement = row - if 'width' not in info: - if threed: - width = len(row) - else: - width = len(row) // planes - info['width'] = width - - # Not implemented yet - assert not threed - - if 'bitdepth' not in info: - try: - dtype = testelement.dtype - # goto the "else:" clause. Sorry. - except: - try: - # Try a Python array.array. - bitdepth = 8 * testelement.itemsize - except: - # We can't determine it from the array element's - # datatype, use a default of 8. - bitdepth = 8 - else: - # If we got here without exception, we now assume that - # the array is a numpy array. - if dtype.kind == 'b': - bitdepth = 1 - else: - bitdepth = 8 * dtype.itemsize - info['bitdepth'] = bitdepth - - for thing in 'width height bitdepth greyscale alpha'.split(): - assert thing in info - return Image(a, info) - -# So that refugee's from PIL feel more at home. Not documented. -fromarray = from_array - -class Image: - """A PNG image. - You can create an :class:`Image` object from an array of pixels by calling - :meth:`png.from_array`. It can be saved to disk with the - :meth:`save` method.""" - def __init__(self, rows, info): - """ - .. note :: - - The constructor is not public. Please do not call it. - """ - - self.rows = rows - self.info = info - - def save(self, file): - """Save the image to *file*. If *file* looks like an open file - descriptor then it is used, otherwise it is treated as a - filename and a fresh file is opened. - - In general, you can only call this method once; after it has - been called the first time and the PNG image has been saved, the - source data will have been streamed, and cannot be streamed - again. - """ - - w = Writer(**self.info) - - try: - file.write - def close(): pass - except: - file = open(file, 'wb') - def close(): file.close() - - try: - w.write(file, self.rows) - finally: - close() - -class _readable: - """ - A simple file-like interface for strings and arrays. - """ - - def __init__(self, buf): - self.buf = buf - self.offset = 0 - - def read(self, n): - r = self.buf[self.offset:self.offset+n] - if isarray(r): - r = r.tostring() - self.offset += n - return r - - -class Reader: - """ - PNG decoder in pure Python. - """ - - def __init__(self, _guess=None, **kw): - """ - Create a PNG decoder object. - - The constructor expects exactly one keyword argument. If you - supply a positional argument instead, it will guess the input - type. You can choose among the following keyword arguments: - - filename - Name of input file (a PNG file). - file - A file-like object (object with a read() method). - bytes - ``array`` or ``string`` with PNG data. - - """ - if ((_guess is not None and len(kw) != 0) or - (_guess is None and len(kw) != 1)): - raise TypeError("Reader() takes exactly 1 argument") - - # Will be the first 8 bytes, later on. See validate_signature. - self.signature = None - self.transparent = None - # A pair of (len,type) if a chunk has been read but its data and - # checksum have not (in other words the file position is just - # past the 4 bytes that specify the chunk type). See preamble - # method for how this is used. - self.atchunk = None - - if _guess is not None: - if isarray(_guess): - kw["bytes"] = _guess - elif isinstance(_guess, str): - kw["filename"] = _guess - elif isinstance(_guess, file): - kw["file"] = _guess - - if "filename" in kw: - self.file = open(kw["filename"], "rb") - elif "file" in kw: - self.file = kw["file"] - elif "bytes" in kw: - self.file = _readable(kw["bytes"]) - else: - raise TypeError("expecting filename, file or bytes array") - - def chunk(self, seek=None): - """ - Read the next PNG chunk from the input file; returns a - (*type*,*data*) tuple. *type* is the chunk's type as a string - (all PNG chunk types are 4 characters long). *data* is the - chunk's data content, as a string. - - If the optional `seek` argument is - specified then it will keep reading chunks until it either runs - out of file or finds the type specified by the argument. Note - that in general the order of chunks in PNGs is unspecified, so - using `seek` can cause you to miss chunks. - """ - - self.validate_signature() - - while True: - # http://www.w3.org/TR/PNG/#5Chunk-layout - if not self.atchunk: - self.atchunk = self.chunklentype() - length,type = self.atchunk - self.atchunk = None - data = self.file.read(length) - if len(data) != length: - raise ChunkError('Chunk %s too short for required %i octets.' - % (type, length)) - checksum = self.file.read(4) - if len(checksum) != 4: - raise ValueError('Chunk %s too short for checksum.', tag) - if seek and type != seek: - continue - verify = zlib.crc32(strtobytes(type)) - verify = zlib.crc32(data, verify) - # Whether the output from zlib.crc32 is signed or not varies - # according to hideous implementation details, see - # http://bugs.python.org/issue1202 . - # We coerce it to be positive here (in a way which works on - # Python 2.3 and older). - verify &= 2**32 - 1 - verify = struct.pack('!I', verify) - if checksum != verify: - # print repr(checksum) - (a, ) = struct.unpack('!I', checksum) - (b, ) = struct.unpack('!I', verify) - raise ChunkError( - "Checksum error in %s chunk: 0x%08X != 0x%08X." % - (type, a, b)) - return type, data - - def chunks(self): - """Return an iterator that will yield each chunk as a - (*chunktype*, *content*) pair. - """ - - while True: - t,v = self.chunk() - yield t,v - if t == 'IEND': - break - - def undo_filter(self, filter_type, scanline, previous): - """Undo the filter for a scanline. `scanline` is a sequence of - bytes that does not include the initial filter type byte. - `previous` is decoded previous scanline (for straightlaced - images this is the previous pixel row, but for interlaced - images, it is the previous scanline in the reduced image, which - in general is not the previous pixel row in the final image). - When there is no previous scanline (the first row of a - straightlaced image, or the first row in one of the passes in an - interlaced image), then this argument should be ``None``. - - The scanline will have the effects of filtering removed, and the - result will be returned as a fresh sequence of bytes. - """ - - # :todo: Would it be better to update scanline in place? - - # Create the result byte array. It seems that the best way to - # create the array to be the right size is to copy from an - # existing sequence. *sigh* - # If we fill the result with scanline, then this allows a - # micro-optimisation in the "null" and "sub" cases. - result = array('B', scanline) - - if filter_type == 0: - # And here, we _rely_ on filling the result with scanline, - # above. - return result - - if filter_type not in (1,2,3,4): - raise FormatError('Invalid PNG Filter Type.' - ' See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .') - - # Filter unit. The stride from one pixel to the corresponding - # byte from the previous previous. Normally this is the pixel - # size in bytes, but when this is smaller than 1, the previous - # byte is used instead. - fu = max(1, self.psize) - - # For the first line of a pass, synthesize a dummy previous - # line. An alternative approach would be to observe that on the - # first line 'up' is the same as 'null', 'paeth' is the same - # as 'sub', with only 'average' requiring any special case. - if not previous: - previous = array('B', [0]*len(scanline)) - - def sub(): - """Undo sub filter.""" - - ai = 0 - # Loops starts at index fu. Observe that the initial part - # of the result is already filled in correctly with - # scanline. - for i in range(fu, len(result)): - x = scanline[i] - a = result[ai] - result[i] = (x + a) & 0xff - ai += 1 - - def up(): - """Undo up filter.""" - - for i in range(len(result)): - x = scanline[i] - b = previous[i] - result[i] = (x + b) & 0xff - - def average(): - """Undo average filter.""" - - ai = -fu - for i in range(len(result)): - x = scanline[i] - if ai < 0: - a = 0 - else: - a = result[ai] - b = previous[i] - result[i] = (x + ((a + b) >> 1)) & 0xff - ai += 1 - - def paeth(): - """Undo Paeth filter.""" - - # Also used for ci. - ai = -fu - for i in range(len(result)): - x = scanline[i] - if ai < 0: - a = c = 0 - else: - a = result[ai] - c = previous[ai] - b = previous[i] - p = a + b - c - pa = abs(p - a) - pb = abs(p - b) - pc = abs(p - c) - if pa <= pb and pa <= pc: - pr = a - elif pb <= pc: - pr = b - else: - pr = c - result[i] = (x + pr) & 0xff - ai += 1 - - # Call appropriate filter algorithm. Note that 0 has already - # been dealt with. - (None, sub, up, average, paeth)[filter_type]() - return result - - def deinterlace(self, raw): - """ - Read raw pixel data, undo filters, deinterlace, and flatten. - Return in flat row flat pixel format. - """ - - # print >> sys.stderr, ("Reading interlaced, w=%s, r=%s, planes=%s," + - # " bpp=%s") % (self.width, self.height, self.planes, self.bps) - # Values per row (of the target image) - vpr = self.width * self.planes - - # Make a result array, and make it big enough. Interleaving - # writes to the output array randomly (well, not quite), so the - # entire output array must be in memory. - fmt = 'BH'[self.bitdepth > 8] - a = array(fmt, [0]*vpr*self.height) - source_offset = 0 - - for xstart, ystart, xstep, ystep in _adam7: - # print >> sys.stderr, "Adam7: start=%s,%s step=%s,%s" % ( - # xstart, ystart, xstep, ystep) - if xstart >= self.width: - continue - # The previous (reconstructed) scanline. None at the - # beginning of a pass to indicate that there is no previous - # line. - recon = None - # Pixels per row (reduced pass image) - ppr = int(math.ceil((self.width-xstart)/float(xstep))) - # Row size in bytes for this pass. - row_size = int(math.ceil(self.psize * ppr)) - for y in range(ystart, self.height, ystep): - filter_type = raw[source_offset] - source_offset += 1 - scanline = raw[source_offset:source_offset+row_size] - source_offset += row_size - recon = self.undo_filter(filter_type, scanline, recon) - # Convert so that there is one element per pixel value - flat = self.serialtoflat(recon, ppr) - if xstep == 1: - assert xstart == 0 - offset = y * vpr - a[offset:offset+vpr] = flat - else: - offset = y * vpr + xstart * self.planes - end_offset = (y+1) * vpr - skip = self.planes * xstep - for i in range(self.planes): - a[offset+i:end_offset:skip] = \ - flat[i::self.planes] - return a - - def iterboxed(self, rows): - """Iterator that yields each scanline in boxed row flat pixel - format. `rows` should be an iterator that yields the bytes of - each row in turn. - """ - - def asvalues(raw): - """Convert a row of raw bytes into a flat row. Result may - or may not share with argument""" - - if self.bitdepth == 8: - return raw - if self.bitdepth == 16: - raw = tostring(raw) - return array('H', struct.unpack('!%dH' % (len(raw)//2), raw)) - assert self.bitdepth < 8 - width = self.width - # Samples per byte - spb = 8//self.bitdepth - out = array('B') - mask = 2**self.bitdepth - 1 - shifts = list(map(self.bitdepth.__mul__, reversed(list(range(spb))))) - for o in raw: - out.extend([mask&(o>>i) for i in shifts]) - return out[:width] - - return map(asvalues, rows) - - def serialtoflat(self, bytes, width=None): - """Convert serial format (byte stream) pixel data to flat row - flat pixel. - """ - - if self.bitdepth == 8: - return bytes - if self.bitdepth == 16: - bytes = tostring(bytes) - return array('H', - struct.unpack('!%dH' % (len(bytes)//2), bytes)) - assert self.bitdepth < 8 - if width is None: - width = self.width - # Samples per byte - spb = 8//self.bitdepth - out = array('B') - mask = 2**self.bitdepth - 1 - shifts = list(map(self.bitdepth.__mul__, reversed(list(range(spb))))) - l = width - for o in bytes: - out.extend([(mask&(o>>s)) for s in shifts][:l]) - l -= spb - if l <= 0: - l = width - return out - - def iterstraight(self, raw): - """Iterator that undoes the effect of filtering, and yields each - row in serialised format (as a sequence of bytes). Assumes input - is straightlaced. `raw` should be an iterable that yields the - raw bytes in chunks of arbitrary size.""" - - # length of row, in bytes - rb = self.row_bytes - a = array('B') - # The previous (reconstructed) scanline. None indicates first - # line of image. - recon = None - for some in raw: - a.extend(some) - while len(a) >= rb + 1: - filter_type = a[0] - scanline = a[1:rb+1] - del a[:rb+1] - recon = self.undo_filter(filter_type, scanline, recon) - yield recon - if len(a) != 0: - # :file:format We get here with a file format error: when the - # available bytes (after decompressing) do not pack into exact - # rows. - raise FormatError( - 'Wrong size for decompressed IDAT chunk.') - assert len(a) == 0 - - def validate_signature(self): - """If signature (header) has not been read then read and - validate it; otherwise do nothing. - """ - - if self.signature: - return - self.signature = self.file.read(8) - if self.signature != _signature: - raise FormatError("PNG file has invalid signature.") - - def preamble(self): - """ - Extract the image metadata by reading the initial part of the PNG - file up to the start of the ``IDAT`` chunk. All the chunks that - precede the ``IDAT`` chunk are read and either processed for - metadata or discarded. - """ - - self.validate_signature() - - while True: - if not self.atchunk: - self.atchunk = self.chunklentype() - if self.atchunk is None: - raise FormatError( - 'This PNG file has no IDAT chunks.') - if self.atchunk[1] == 'IDAT': - return - self.process_chunk() - - def chunklentype(self): - """Reads just enough of the input to determine the next - chunk's length and type, returned as a (*length*, *type*) pair - where *type* is a string. If there are no more chunks, ``None`` - is returned. - """ - - x = self.file.read(8) - if not x: - return None - if len(x) != 8: - raise FormatError( - 'End of file whilst reading chunk length and type.') - length,type = struct.unpack('!I4s', x) - type = bytestostr(type) - if length > 2**31-1: - raise FormatError('Chunk %s is too large: %d.' % (type,length)) - return length,type - - def process_chunk(self): - """Process the next chunk and its data. This only processes the - following chunk types, all others are ignored: ``IHDR``, - ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``. - """ - - type, data = self.chunk() - if type == 'IHDR': - # http://www.w3.org/TR/PNG/#11IHDR - if len(data) != 13: - raise FormatError('IHDR chunk has incorrect length.') - (self.width, self.height, self.bitdepth, self.color_type, - self.compression, self.filter, - self.interlace) = struct.unpack("!2I5B", data) - - # Check that the header specifies only valid combinations. - if self.bitdepth not in (1,2,4,8,16): - raise Error("invalid bit depth %d" % self.bitdepth) - if self.color_type not in (0,2,3,4,6): - raise Error("invalid colour type %d" % self.color_type) - # Check indexed (palettized) images have 8 or fewer bits - # per pixel; check only indexed or greyscale images have - # fewer than 8 bits per pixel. - if ((self.color_type & 1 and self.bitdepth > 8) or - (self.bitdepth < 8 and self.color_type not in (0,3))): - raise FormatError("Illegal combination of bit depth (%d)" - " and colour type (%d)." - " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ." - % (self.bitdepth, self.color_type)) - if self.compression != 0: - raise Error("unknown compression method %d" % self.compression) - if self.filter != 0: - raise FormatError("Unknown filter method %d," - " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ." - % self.filter) - if self.interlace not in (0,1): - raise FormatError("Unknown interlace method %d," - " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ." - % self.interlace) - - # Derived values - # http://www.w3.org/TR/PNG/#6Colour-values - colormap = bool(self.color_type & 1) - greyscale = not (self.color_type & 2) - alpha = bool(self.color_type & 4) - color_planes = (3,1)[greyscale or colormap] - planes = color_planes + alpha - - self.colormap = colormap - self.greyscale = greyscale - self.alpha = alpha - self.color_planes = color_planes - self.planes = planes - self.psize = float(self.bitdepth)/float(8) * planes - if int(self.psize) == self.psize: - self.psize = int(self.psize) - self.row_bytes = int(math.ceil(self.width * self.psize)) - # Stores PLTE chunk if present, and is used to check - # chunk ordering constraints. - self.plte = None - # Stores tRNS chunk if present, and is used to check chunk - # ordering constraints. - self.trns = None - # Stores sbit chunk if present. - self.sbit = None - elif type == 'PLTE': - # http://www.w3.org/TR/PNG/#11PLTE - if self.plte: - warnings.warn("Multiple PLTE chunks present.") - self.plte = data - if len(data) % 3 != 0: - raise FormatError( - "PLTE chunk's length should be a multiple of 3.") - if len(data) > (2**self.bitdepth)*3: - raise FormatError("PLTE chunk is too long.") - if len(data) == 0: - raise FormatError("Empty PLTE is not allowed.") - elif type == 'bKGD': - try: - if self.colormap: - if not self.plte: - warnings.warn( - "PLTE chunk is required before bKGD chunk.") - self.background = struct.unpack('B', data) - else: - self.background = struct.unpack("!%dH" % self.color_planes, - data) - except struct.error: - raise FormatError("bKGD chunk has incorrect length.") - elif type == 'tRNS': - # http://www.w3.org/TR/PNG/#11tRNS - self.trns = data - if self.colormap: - if not self.plte: - warnings.warn("PLTE chunk is required before tRNS chunk.") - else: - if len(data) > len(self.plte)/3: - # Was warning, but promoted to Error as it - # would otherwise cause pain later on. - raise FormatError("tRNS chunk is too long.") - else: - if self.alpha: - raise FormatError( - "tRNS chunk is not valid with colour type %d." % - self.color_type) - try: - self.transparent = \ - struct.unpack("!%dH" % self.color_planes, data) - except struct.error: - raise FormatError("tRNS chunk has incorrect length.") - elif type == 'gAMA': - try: - self.gamma = struct.unpack("!L", data)[0] / 100000.0 - except struct.error: - raise FormatError("gAMA chunk has incorrect length.") - elif type == 'sBIT': - self.sbit = data - if (self.colormap and len(data) != 3 or - not self.colormap and len(data) != self.planes): - raise FormatError("sBIT chunk has incorrect length.") - - def read(self): - """ - Read the PNG file and decode it. Returns (`width`, `height`, - `pixels`, `metadata`). - - May use excessive memory. - - `pixels` are returned in boxed row flat pixel format. - """ - - def iteridat(): - """Iterator that yields all the ``IDAT`` chunks as strings.""" - while True: - try: - type, data = self.chunk() - except ValueError as e: - raise ChunkError(e.args[0]) - if type == 'IEND': - # http://www.w3.org/TR/PNG/#11IEND - break - if type != 'IDAT': - continue - # type == 'IDAT' - # http://www.w3.org/TR/PNG/#11IDAT - if self.colormap and not self.plte: - warnings.warn("PLTE chunk is required before IDAT chunk") - yield data - - def iterdecomp(idat): - """Iterator that yields decompressed strings. `idat` should - be an iterator that yields the ``IDAT`` chunk data. - """ - - # Currently, with no max_length paramter to decompress, this - # routine will do one yield per IDAT chunk. So not very - # incremental. - d = zlib.decompressobj() - # Each IDAT chunk is passed to the decompressor, then any - # remaining state is decompressed out. - for data in idat: - # :todo: add a max_length argument here to limit output - # size. - yield array('B', d.decompress(data)) - yield array('B', d.flush()) - - self.preamble() - raw = iterdecomp(iteridat()) - - if self.interlace: - raw = array('B', itertools.chain(*raw)) - arraycode = 'BH'[self.bitdepth>8] - # Like :meth:`group` but producing an array.array object for - # each row. - pixels = map(lambda *row: array(arraycode, row), - *[iter(self.deinterlace(raw))]*self.width*self.planes) - else: - pixels = self.iterboxed(self.iterstraight(raw)) - meta = dict() - for attr in 'greyscale alpha planes bitdepth interlace'.split(): - meta[attr] = getattr(self, attr) - meta['size'] = (self.width, self.height) - for attr in 'gamma transparent background'.split(): - a = getattr(self, attr, None) - if a is not None: - meta[attr] = a - return self.width, self.height, pixels, meta - - - def read_flat(self): - """ - Read a PNG file and decode it into flat row flat pixel format. - Returns (*width*, *height*, *pixels*, *metadata*). - - May use excessive memory. - - `pixels` are returned in flat row flat pixel format. - - See also the :meth:`read` method which returns pixels in the - more stream-friendly boxed row flat pixel format. - """ - - x, y, pixel, meta = self.read() - arraycode = 'BH'[meta['bitdepth']>8] - pixel = array(arraycode, itertools.chain(*pixel)) - return x, y, pixel, meta - - def palette(self, alpha='natural'): - """Returns a palette that is a sequence of 3-tuples or 4-tuples, - synthesizing it from the ``PLTE`` and ``tRNS`` chunks. These - chunks should have already been processed (for example, by - calling the :meth:`preamble` method). All the tuples are the - same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when - there is a ``tRNS`` chunk. Assumes that the image is colour type - 3 and therefore a ``PLTE`` chunk is required. - - If the `alpha` argument is ``'force'`` then an alpha channel is - always added, forcing the result to be a sequence of 4-tuples. - """ - - if not self.plte: - raise FormatError( - "Required PLTE chunk is missing in colour type 3 image.") - plte = group(array('B', self.plte), 3) - if self.trns or alpha == 'force': - trns = array('B', self.trns or '') - trns.extend([255]*(len(plte)-len(trns))) - plte = list(map(operator.add, plte, group(trns, 1))) - return plte - - def asDirect(self): - """Returns the image data as a direct representation of an - ``x * y * planes`` array. This method is intended to remove the - need for callers to deal with palettes and transparency - themselves. Images with a palette (colour type 3) - are converted to RGB or RGBA; images with transparency (a - ``tRNS`` chunk) are converted to LA or RGBA as appropriate. - When returned in this format the pixel values represent the - colour value directly without needing to refer to palettes or - transparency information. - - Like the :meth:`read` method this method returns a 4-tuple: - - (*width*, *height*, *pixels*, *meta*) - - This method normally returns pixel values with the bit depth - they have in the source image, but when the source PNG has an - ``sBIT`` chunk it is inspected and can reduce the bit depth of - the result pixels; pixel values will be reduced according to - the bit depth specified in the ``sBIT`` chunk (PNG nerds should - note a single result bit depth is used for all channels; the - maximum of the ones specified in the ``sBIT`` chunk. An RGB565 - image will be rescaled to 6-bit RGB666). - - The *meta* dictionary that is returned reflects the `direct` - format and not the original source image. For example, an RGB - source image with a ``tRNS`` chunk to represent a transparent - colour, will have ``planes=3`` and ``alpha=False`` for the - source image, but the *meta* dictionary returned by this method - will have ``planes=4`` and ``alpha=True`` because an alpha - channel is synthesized and added. - - *pixels* is the pixel data in boxed row flat pixel format (just - like the :meth:`read` method). - - All the other aspects of the image data are not changed. - """ - - self.preamble() - - # Simple case, no conversion necessary. - if not self.colormap and not self.trns and not self.sbit: - return self.read() - - x,y,pixels,meta = self.read() - - if self.colormap: - meta['colormap'] = False - meta['alpha'] = bool(self.trns) - meta['bitdepth'] = 8 - meta['planes'] = 3 + bool(self.trns) - plte = self.palette() - def iterpal(pixels): - for row in pixels: - row = list(map(plte.__getitem__, row)) - yield array('B', itertools.chain(*row)) - pixels = iterpal(pixels) - elif self.trns: - # It would be nice if there was some reasonable way of doing - # this without generating a whole load of intermediate tuples. - # But tuples does seem like the easiest way, with no other way - # clearly much simpler or much faster. (Actually, the L to LA - # conversion could perhaps go faster (all those 1-tuples!), but - # I still wonder whether the code proliferation is worth it) - it = self.transparent - maxval = 2**meta['bitdepth']-1 - planes = meta['planes'] - meta['alpha'] = True - meta['planes'] += 1 - typecode = 'BH'[meta['bitdepth']>8] - def itertrns(pixels): - for row in pixels: - # For each row we group it into pixels, then form a - # characterisation vector that says whether each pixel - # is opaque or not. Then we convert True/False to - # 0/maxval (by multiplication), and add it as the extra - # channel. - row = group(row, planes) - opa = list(map(it.__ne__, row)) - opa = list(map(maxval.__mul__, opa)) - opa = list(zip(opa)) # convert to 1-tuples - yield array(typecode, - itertools.chain(*list(map(operator.add, row, opa)))) - pixels = itertrns(pixels) - targetbitdepth = None - if self.sbit: - sbit = struct.unpack('%dB' % len(self.sbit), self.sbit) - targetbitdepth = max(sbit) - if targetbitdepth > meta['bitdepth']: - raise Error('sBIT chunk %r exceeds bitdepth %d' % - (sbit,self.bitdepth)) - if min(sbit) <= 0: - raise Error('sBIT chunk %r has a 0-entry' % sbit) - if targetbitdepth == meta['bitdepth']: - targetbitdepth = None - if targetbitdepth: - shift = meta['bitdepth'] - targetbitdepth - meta['bitdepth'] = targetbitdepth - def itershift(pixels): - for row in pixels: - yield list(map(shift.__rrshift__, row)) - pixels = itershift(pixels) - return x,y,pixels,meta - - def asFloat(self, maxval=1.0): - """Return image pixels as per :meth:`asDirect` method, but scale - all pixel values to be floating point values between 0.0 and - *maxval*. - """ - - x,y,pixels,info = self.asDirect() - sourcemaxval = 2**info['bitdepth']-1 - del info['bitdepth'] - info['maxval'] = float(maxval) - factor = float(maxval)/float(sourcemaxval) - def iterfloat(): - for row in pixels: - yield list(map(factor.__mul__, row)) - return x,y,iterfloat(),info - - def _as_rescale(self, get, targetbitdepth): - """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`.""" - - width,height,pixels,meta = get() - maxval = 2**meta['bitdepth'] - 1 - targetmaxval = 2**targetbitdepth - 1 - factor = float(targetmaxval) / float(maxval) - meta['bitdepth'] = targetbitdepth - def iterscale(): - for row in pixels: - yield [int(round(x*factor)) for x in row] - return width, height, iterscale(), meta - - def asRGB8(self): - """Return the image data as an RGB pixels with 8-bits per - sample. This is like the :meth:`asRGB` method except that - this method additionally rescales the values so that they - are all between 0 and 255 (8-bit). In the case where the - source image has a bit depth < 8 the transformation preserves - all the information; where the source image has bit depth - > 8, then rescaling to 8-bit values loses precision. No - dithering is performed. Like :meth:`asRGB`, an alpha channel - in the source image will raise an exception. - - This function returns a 4-tuple: - (*width*, *height*, *pixels*, *metadata*). - *width*, *height*, *metadata* are as per the :meth:`read` method. - - *pixels* is the pixel data in boxed row flat pixel format. - """ - - return self._as_rescale(self.asRGB, 8) - - def asRGBA8(self): - """Return the image data as RGBA pixels with 8-bits per - sample. This method is similar to :meth:`asRGB8` and - :meth:`asRGBA`: The result pixels have an alpha channel, *and* - values are rescaled to the range 0 to 255. The alpha channel is - synthesized if necessary (with a small speed penalty). - """ - - return self._as_rescale(self.asRGBA, 8) - - def asRGB(self): - """Return image as RGB pixels. RGB colour images are passed - through unchanged; greyscales are expanded into RGB - triplets (there is a small speed overhead for doing this). - - An alpha channel in the source image will raise an - exception. - - The return values are as for the :meth:`read` method - except that the *metadata* reflect the returned pixels, not the - source image. In particular, for this method - ``metadata['greyscale']`` will be ``False``. - """ - - width,height,pixels,meta = self.asDirect() - if meta['alpha']: - raise Error("will not convert image with alpha channel to RGB") - if not meta['greyscale']: - return width,height,pixels,meta - meta['greyscale'] = False - typecode = 'BH'[meta['bitdepth'] > 8] - def iterrgb(): - for row in pixels: - a = array(typecode, [0]) * 3 * width - for i in range(3): - a[i::3] = row - yield a - return width,height,iterrgb(),meta - - def asRGBA(self): - """Return image as RGBA pixels. Greyscales are expanded into - RGB triplets; an alpha channel is synthesized if necessary. - The return values are as for the :meth:`read` method - except that the *metadata* reflect the returned pixels, not the - source image. In particular, for this method - ``metadata['greyscale']`` will be ``False``, and - ``metadata['alpha']`` will be ``True``. - """ - - width,height,pixels,meta = self.asDirect() - if meta['alpha'] and not meta['greyscale']: - return width,height,pixels,meta - typecode = 'BH'[meta['bitdepth'] > 8] - maxval = 2**meta['bitdepth'] - 1 - def newarray(): - return array(typecode, [0]) * 4 * width - if meta['alpha'] and meta['greyscale']: - # LA to RGBA - def convert(): - for row in pixels: - # Create a fresh target row, then copy L channel - # into first three target channels, and A channel - # into fourth channel. - a = newarray() - for i in range(3): - a[i::4] = row[0::2] - a[3::4] = row[1::2] - yield a - elif meta['greyscale']: - # L to RGBA - def convert(): - for row in pixels: - a = newarray() - for i in range(3): - a[i::4] = row - a[3::4] = array(typecode, [maxval]) * width - yield a - else: - assert not meta['alpha'] and not meta['greyscale'] - # RGB to RGBA - def convert(): - for row in pixels: - a = newarray() - for i in range(3): - a[i::4] = row[i::3] - a[3::4] = array(typecode, [maxval]) * width - yield a - meta['alpha'] = True - meta['greyscale'] = False - return width,height,convert(),meta - diff --git a/gramps/webapp/grampsdb/view/repository.py b/gramps/webapp/grampsdb/view/repository.py deleted file mode 100644 index 1eb76a5a3..000000000 --- a/gramps/webapp/grampsdb/view/repository.py +++ /dev/null @@ -1,126 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search -from gramps.webapp.grampsdb.models import Repository -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_repository(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Repository") - context["tviews"] = _("Repositories") - context["action"] = "view" - view_template = "view_repository_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick repository", - Repository, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick repository", - Repository, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Repository.objects.get(handle=ref_handle) - dji.add_repository_ref_default(parent_obj, ref_obj) - parent_obj.save_cache() # rebuild cache - return redirect("/%s/%s%s#tab-repositories" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "add": - repository = Repository(gramps_id=dji.get_next_id(Repository, "R")) - repositoryform = RepositoryForm(instance=repository) - repositoryform.model = repository - elif act in ["view", "edit"]: - repository = Repository.objects.get(handle=handle) - repositoryform = RepositoryForm(instance=repository) - repositoryform.model = repository - elif act == "save": - repository = Repository.objects.get(handle=handle) - repositoryform = RepositoryForm(request.POST, instance=repository) - repositoryform.model = repository - if repositoryform.is_valid(): - update_last_changed(repository, request.user.username) - repository = repositoryform.save() - act = "view" - else: - act = "edit" - elif act == "create": - repository = Repository(handle=create_id()) - repositoryform = RepositoryForm(request.POST, instance=repository) - repositoryform.model = repository - if repositoryform.is_valid(): - update_last_changed(repository, request.user.username) - repository = repositoryform.save() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_repository_ref_default(obj, repository) - obj.save_cache() - return redirect("/%s/%s#tab-repositories" % (item, handle)) - act = "view" - else: - act = "add" - elif act == "delete": - repository = Repository.objects.get(handle=handle) - repository.delete() - return redirect("/repository/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["repositoryform"] = repositoryform - context["object"] = repository - context["repository"] = repository - context["action"] = act - - return render_to_response(view_template, context) - diff --git a/gramps/webapp/grampsdb/view/source.py b/gramps/webapp/grampsdb/view/source.py deleted file mode 100644 index da1c2d041..000000000 --- a/gramps/webapp/grampsdb/view/source.py +++ /dev/null @@ -1,125 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed, build_search -from gramps.webapp.grampsdb.models import Source -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_source(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Source") - context["tviews"] = _("Sources") - context["action"] = "view" - view_template = "view_source_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete, share, save-share - if act == "share": - item, handle = add_to - context["pickform"] = PickForm("Pick source", - Source, - (), - request.POST) - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "save-share": - item, handle = add_to - pickform = PickForm("Pick source", - Source, - (), - request.POST) - if pickform.data["picklist"]: - parent_model = dji.get_model(item) # what model? - parent_obj = parent_model.objects.get(handle=handle) # to add - ref_handle = pickform.data["picklist"] - ref_obj = Source.objects.get(handle=ref_handle) - dji.add_source_ref_default(parent_obj, ref_obj) - parent_obj.save_cache() # rebuild cache - return redirect("/%s/%s%s#tab-sources" % (item, handle, build_search(request))) - else: - context["pickform"] = pickform - context["object_handle"] = handle - context["object_type"] = item - return render_to_response("pick.html", context) - elif act == "add": - source = Source(gramps_id=dji.get_next_id(Source, "S")) - sourceform = SourceForm(instance=source) - sourceform.model = source - elif act in ["view", "edit"]: - source = Source.objects.get(handle=handle) - sourceform = SourceForm(instance=source) - sourceform.model = source - elif act == "save": - source = Source.objects.get(handle=handle) - sourceform = SourceForm(request.POST, instance=source) - sourceform.model = source - if sourceform.is_valid(): - update_last_changed(source, request.user.username) - source = sourceform.save() - dji.rebuild_cache(source) - act = "view" - else: - act = "edit" - elif act == "create": - source = Source(handle=create_id()) - sourceform = SourceForm(request.POST, instance=source) - sourceform.model = source - if sourceform.is_valid(): - update_last_changed(source, request.user.username) - source = sourceform.save() - dji.rebuild_cache(source) - if add_to: - raise Exception("Cannot add reference") - act = "view" - else: - act = "add" - elif act == "delete": - source = Source.objects.get(handle=handle) - source.delete() - return redirect("/source/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["sourceform"] = sourceform - context["object"] = source - context["source"] = source - context["action"] = act - - return render_to_response(view_template, context) - - - diff --git a/gramps/webapp/grampsdb/view/tag.py b/gramps/webapp/grampsdb/view/tag.py deleted file mode 100644 index e3531ce85..000000000 --- a/gramps/webapp/grampsdb/view/tag.py +++ /dev/null @@ -1,97 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Views for Person, Name, and Surname """ - -## Gramps Modules -from gramps.webapp.utils import _, boolean, update_last_changed -from gramps.webapp.grampsdb.models import Tag -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.libdjango import DjangoInterface - -## Django Modules -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext - -## Globals -dji = DjangoInterface() - -def process_tag(request, context, handle, act, add_to=None): # view, edit, save - """ - Process act on person. Can return a redirect. - """ - context["tview"] = _("Tag") - context["tviews"] = _("Tags") - context["action"] = "view" - view_template = "view_tag_detail.html" - - if handle == "add": - act = "add" - if "action" in request.POST: - act = request.POST.get("action") - - # Handle: edit, view, add, create, save, delete - if act == "add": - tag = Tag() - tagform = TagForm(instance=tag) - tagform.model = tag - elif act in ["view", "edit"]: - tag = Tag.objects.get(handle=handle) - tagform = TagForm(instance=tag) - tagform.model = tag - elif act == "save": - tag = Tag.objects.get(handle=handle) - tagform = TagForm(request.POST, instance=tag) - tagform.model = tag - if tagform.is_valid(): - update_last_changed(tag, request.user.username) - tag = tagform.save() - act = "view" - else: - act = "edit" - elif act == "create": - tag = Tag(handle=create_id()) - tagform = TagForm(request.POST, instance=tag) - tagform.model = tag - if tagform.is_valid(): - update_last_changed(tag, request.user.username) - tag = tagform.save() - if add_to: - item, handle = add_to - model = dji.get_model(item) - obj = model.objects.get(handle=handle) - dji.add_tag_ref_default(obj, tag) - obj.save_cache() - return redirect("/%s/%s#tab-tags" % (item, handle)) - act = "view" - else: - act = "add" - elif act == "delete": - tag = Tag.objects.get(handle=handle) - tag.delete() - return redirect("/tag/") - else: - raise Exception("Unhandled act: '%s'" % act) - - context["tagform"] = tagform - context["object"] = tag - context["tag"] = tag - context["action"] = act - - return render_to_response(view_template, context) diff --git a/gramps/webapp/grampsdb/views.py b/gramps/webapp/grampsdb/views.py deleted file mode 100644 index 2c6ee24b4..000000000 --- a/gramps/webapp/grampsdb/views.py +++ /dev/null @@ -1,1589 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" -Main view handlers -Each object can be operated on with the following actions: - view: show the data - delete: delete the object (FIXME: needs undo) - edit: show the data in its editing widget - save: action in the form in edit mode; write data to db - add: show blank data in their editing widget - create: action in the form in edit mode; add new data to db -""" - -import os -import time -import pickle -import base64 - -#------------------------------------------------------------------------ -# -# Django Modules -# -#------------------------------------------------------------------------ -from django.contrib.auth import logout -from django.contrib.auth.models import User -from django.core.paginator import Paginator, InvalidPage, EmptyPage -from django.http import Http404, HttpResponseRedirect, HttpResponse -from django.shortcuts import get_object_or_404, render_to_response, redirect -from django.template import Context, RequestContext -from django.db.models import Q -from django.forms.models import modelformset_factory -import simplejson - -#------------------------------------------------------------------------ -# -# Gramps Modules -# -#------------------------------------------------------------------------ -from gramps.version import VERSION - -# Gramps-connect imports: -import gramps.webapp -from gramps.webapp.utils import _, build_args, db -from gramps.webapp.grampsdb.models import * -from gramps.webapp.grampsdb.view import * -from gramps.webapp.djangodb import DbDjango -import gramps.cli.user - -# Menu: (, //, | None, Need authentication ) -MENU = [ - (_('Browse'), 'browse', None, False), - (_('Reports'), 'report', Report, True), - (_('User'), 'user', None, True), -] -# Views: [(, //handle, ), ] -VIEWS = [ - (_('People'), 'person', Name), - (_('Families'), 'family', Family), - (_('Events'), 'event', Event), - (_('Notes'), 'note', Note), - (_('Media'), 'media', Media), - (_('Citations'), 'citation', Citation), - (_('Sources'), 'source', Source), - (_('Places'), 'place', Place), - (_('Repositories'), 'repository', Repository), - (_('Tags'), 'tag', Tag), - ] - -def context_processor(request): - """ - This function is executed before template processing. - takes a request, and returns a dictionary context. - """ - global SITENAME - context = {} - if request.user.is_authenticated(): - profile = request.user.profile - context["css_theme"] = profile.theme_type.name - else: - context["css_theme"] = "Web_Mainz.css" - # Other things for all environments: - context["gramps_version"] = VERSION - context["views"] = VIEWS - context["menu"] = MENU - context["None"] = None - context["True"] = True - context["False"] = False - context["sitename"] = Config.objects.get(setting="sitename").value - context["default"] = "" - - search = request.GET.get("search", "") or request.POST.get("search", "") - page = request.GET.get("page", "") or request.POST.get("page", "") - context["page"] = page - context["search"] = search - context["args"] = build_args(search=search, page=page) - return context - -def main_page(request): - """ - Show the main page. - """ - context = RequestContext(request) - context["view"] = 'home' - context["tview"] = _('Home') - return render_to_response("main_page.html", context) - -def logout_page(request): - """ - Logout a user. - """ - context = RequestContext(request) - context["view"] = 'home' - context["tview"] = _('Home') - logout(request) - return HttpResponseRedirect('/') - -def make_message(request, message): - if request.user.is_authenticated(): - #request.user.message_set.create(message = message) - print("FIXME: message_set:", message) - else: - request.session['message'] = message - -def browse_page(request): - """ - Show the main list under 'Browse' on the main menu. - """ - context = RequestContext(request) - context["view"] = 'browse' - context["tview"] = _('Browse') - return render_to_response('browse_page.html', context) - -def user_page(request, username=None): - """ - Show the user page. - """ - if request.user.is_authenticated(): - if username is None: - profile = request.user.profile - username = profile.user.username - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - raise Http404(_('Requested user not found.')) - context = RequestContext(request) - context["username"] = username - context["view"] = 'user' - context["tview"] = _('User') - return render_to_response('user_page.html', context) - else: - raise Http404(_("Requested page is not accessible.")) - -def timestamp(): - """ - Construct a string of current time for filenames. - """ - return time.strftime("%Y-%m-%d:%H:%M:%S") - -def send_file(request, filename, mimetype): - """ - Send a file through Django without loading the whole file into - memory at once. The FileWrapper will turn the file object into an - iterator for chunks of 8KB. - """ - from django.core.servers.basehttp import FileWrapper - wrapper = FileWrapper(open(filename, mode="rb")) - response = HttpResponse(wrapper, content_type=mimetype) - path, base = os.path.split(filename) - response['Content-Length'] = os.path.getsize(filename) - response['Content-Disposition'] = 'attachment; filename=%s' % base - return response - -def process_report_run(request, handle): - """ - Run a report or export. - """ - # can also use URL with %0A as newline and "=" is "=": - # http://localhost:8000/report/ex_gpkg/run?options=off=gpkg%0Ax=10 - from gramps.webapp.reports import import_file, export_file, download - from gramps.cli.plug import run_report - import traceback - if request.user.is_authenticated(): - profile = request.user.profile - report = Report.objects.get(handle=handle) - args = {"off": "html"} # basic defaults - # override from given defaults in table: - if report.options: - for pair in str(report.options).split("\\n"): - if "=" in pair: - key, value = [x.strip() for x in pair.split("=", 1)] - if key and value: - args[key] = value - # override from options on webpage: - if "options" in request.GET: - options = str(request.GET.get("options")) - if options: - for pair in options.split("\n"): # from webpage - if "=" in pair: - key, value = [x.strip() for x in pair.split("=", 1)] - if key and value: - args[key] = value - ############################################################################# - if report.report_type == "report": - filename = "/tmp/%s-%s-%s.%s" % (str(profile.user.username), str(handle), timestamp(), args["off"]) - run_report(db, handle, of=filename, **args) - mimetype = 'application/%s' % args["off"] - elif report.report_type == "export": - filename = "/tmp/%s-%s-%s.%s" % (str(profile.user.username), str(handle), timestamp(), args["off"]) - export_file(db, filename, gramps.cli.user.User()) # callback - mimetype = 'text/plain' - elif report.report_type == "import": - filename = download(args["i"], "/tmp/%s-%s-%s.%s" % (str(profile.user.username), - str(handle), - timestamp(), - args["iff"])) - if filename is not None: - if True: # run in background, with error handling - import threading - def background(): - try: - import_file(db, filename, gramps.cli.user.User()) # callback - except: - make_message(request, "import_file failed: " + traceback.format_exc()) - threading.Thread(target=background).start() - make_message(request, "Your data is now being imported...") - return redirect("/report/") - else: - success = import_file(db, filename, gramps.cli.user.User()) # callback - if not success: - make_message(request, "Failed to load imported.") - return redirect("/report/") - else: - make_message(request, "No filename was provided or found.") - return redirect("/report/") - else: - make_message(request, "Invalid report type '%s'" % report.report_type) - return redirect("/report/") - # need to wait for the file to exist: - start = time.time() - while not os.path.exists(filename): - # but let's not wait forever: - if time.time() - start > 10: # after 10 seconds, give up! - context = RequestContext(request) - make_message(request, "Failed: '%s' is not found" % filename) - return redirect("/report/") - time.sleep(1) - # FIXME: the following should go into a queue for later presentation - # like a jobs-result queue - if filename.endswith(".html"): - # just give it, perhaps in a new tab - from django.http import HttpResponse - response = HttpResponse(content_type="text/html") - for line in open(filename, mode="rb"): - response.write(line) - return response - else: - return send_file(request, filename, mimetype) - # If failure, just fail for now: - context = RequestContext(request) - context["message"] = "You need to be logged in to run reports." - return render_to_response("main_page.html", context) - -def view_list(request, view): - """ - Borwse each of the primary tables. - """ - context = RequestContext(request) - search = "" - if view == "event": - context["tviews"] = _("Events") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_event_query(request, search) - object_list = Event.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_events.html' - total = Event.objects.all().count() - elif view == "media": - context["tviews"] = _("Media") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_media_query(request, search) - object_list = Media.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_media.html' - total = Media.objects.all().count() - elif view == "note": - context["tviews"] = _("Notes") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_note_query(request, search) - object_list = Note.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_notes.html' - total = Note.objects.all().count() - elif view == "person": - context["tviews"] = _("People") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_person_query(request, search) - object_list = Name.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_people.html' - total = Name.objects.all().count() - elif view == "family": - context["tviews"] = _("Families") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_family_query(request, search) - object_list = Family.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_families.html' - total = Family.objects.all().count() - elif view == "place": - context["tviews"] = _("Places") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_place_query(request, search) - object_list = Place.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_places.html' - total = Place.objects.all().count() - elif view == "repository": - context["tviews"] = _("Repositories") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_repository_query(request, search) - object_list = Repository.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_repositories.html' - total = Repository.objects.all().count() - elif view == "citation": - context["tviews"] = _("Citations") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_citation_query(request, search) - object_list = Citation.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_citations.html' - total = Citation.objects.all().count() - elif view == "source": - context["tviews"] = _("Sources") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_source_query(request, search) - object_list = Source.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_sources.html' - total = Source.objects.all().count() - elif view == "tag": - context["tviews"] = _("Tags") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_tag_query(request, search) - object_list = Tag.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_tags.html' - total = Tag.objects.all().count() - elif view == "report": - context["tviews"] = _("Reports") - search = request.GET.get("search") if "search" in request.GET else "" - query, order, terms = build_report_query(request, search) - object_list = Report.objects \ - .filter(query) \ - .order_by(*order) \ - .distinct() - view_template = 'view_report.html' - total = Report.objects.all().count() - else: - raise Http404("Requested page type '%s' not known" % view) - - if request.user.is_authenticated(): - paginator = Paginator(object_list, 20) - else: - paginator = Paginator(object_list, 20) - - try: - page = int(request.GET.get('page', '1')) - except ValueError: - page = 1 - - try: - page = paginator.page(page) - except (EmptyPage, InvalidPage): - page = paginator.page(paginator.num_pages) - - context["search_terms"] = ", ".join(terms) + "; ^start, end$" - context["page"] = page - context["view"] = view - context["tview"] = _(view.title()) - context["search"] = search - context["total"] = total - context["object_list"] = object_list - context["next"] = "/%s/" % view - if search: - context["search_query"] = ("&search=%s" % search) - else: - context["search_query"] = "" - return render_to_response(view_template, context) - -def check_access(request, context, obj, act): - """ - Check to see if user has access to object. We don't need to - sanitize here, just check to see if we even acknowledge it exists. - """ - if request.user.is_authenticated(): - if request.user.is_superuser: - return True - else: - return act in ["view"] - else: # outside viewer - return not obj.private - -def add_share(request, view, item, handle): - """ - Add a new referenced from . - """ - # /person/share/family/handle - # Use an existing person with this family - # r'^(?P(\w+))/share/(?P(\w+))/(?P(\w+))$', - act = "share" - if "action" in request.POST: - act = request.POST.get("action") # can be "save-share" - return action(request, view, None, act, (item, handle)) - -def add_to(request, view, item, handle): - """ - Add a new referenced from . - """ - # /view/add/person/handle - # /family/add/child/handle - return action(request, view, None, "add", (item, handle)) - -def action(request, view, handle, act, add_to=None): - """ - View a particular object given /object/handle (implied view), - /object/handle/action, or /object/add. - """ - from gramps.webapp.reports import get_plugin_options - # redirect: - rd = None - obj = None - context = RequestContext(request) - if "action" in request.POST: - act = request.POST.get("action") - context["action"] = act - context["view"] = view - context["tview"] = _('Browse') - if view == "event": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Event.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_event_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_event_detail.html' - rd = process_event(request, context, handle, act, add_to) - elif view == "family": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Family.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_family_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_family_detail.html' - rd = process_family(request, context, handle, act, add_to) - elif view == "media": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Media.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_media_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_media_detail.html' - rd = process_media(request, context, handle, act, add_to) - elif view == "note": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Note.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_note_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_note_detail.html' - rd = process_note(request, context, handle, act, add_to) - elif view == "person": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Person.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - person = db.get_person_from_handle(obj.handle) - content = str(person.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_person_detail.html' - rd = process_person(request, context, handle, act, add_to) - elif view == "place": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Place.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_place_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_place_detail.html' - rd = process_place(request, context, handle, act, add_to) - elif view == "repository": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Repository.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_repository_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_repository_detail.html' - rd = process_repository(request, context, handle, act, add_to) - elif view == "citation": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Citation.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_citation_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_citation_detail.html' - rd = process_citation(request, context, handle, act, add_to) - elif view == "source": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Source.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_source_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_source_detail.html' - rd = process_source(request, context, handle, act, add_to) - elif view == "tag": - if act not in ["add", "create", "share", "save-share"]: - try: - obj = Tag.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - if not check_access(request, context, obj, act): - raise Http404(_("Requested %s does not exist.") % view) - if "format" in request.GET: - if request.GET["format"] == "json": - item = db.get_tag_from_handle(obj.handle) - content = str(item.to_struct()) - response = HttpResponse(content, content_type="application/json") - return response - view_template = 'view_tag_detail.html' - rd = process_tag(request, context, handle, act, add_to) - elif view == "report": - if act not in ["add", "create"]: - try: - obj = Report.objects.get(handle=handle) - except: - raise Http404(_("Requested %s does not exist.") % view) - override = {} - if obj.options: - for pair in obj.options.split("\\n"): - key, value = pair.split("=", 1) - override[key] = value - opt_default, opt_help = get_plugin_options(db, obj.handle) - retval = "" - for key in sorted(opt_default.keys()): - if key in override: - retval += "%s=%s\n" % (key, override[key]) - del override[key] - else: - retval += "%s=%s\n" % (key, opt_default[key]) - # Any leftover overrides: - for key in sorted(override.keys()): - retval += "%s=%s\n" % (key, override[key]) - obj.options = retval - retval = "
    " - for key in sorted(opt_help.keys()): - retval += "
  1. %s: %s
  2. \n" % (key, opt_help[key][1]) - retval += "
" - context["help"] = retval - view_template = 'view_report_detail.html' - rd = process_report(request, context, handle, act) - else: - raise Http404(_("Requested page type not known")) - if rd: - return rd - if obj: - context[view] = obj - context["object"] = obj - context["next"] = "/%s/%s" % (view, obj.handle) - return render_to_response(view_template, context) - -def process_report(request, context, handle, act): - """ - Process action on report. Can return a redirect. - """ - if act == "run": - return process_report_run(request, handle) - context["tview"] = _("Report") - context["tviews"] = _("Reports") - -def build_string_query(field, value, exact=False, startswith=False, endswith=False): - retval = None - if exact: - retval = Q(**{"%s" % field: value}) - elif startswith: - retval = Q(**{"%s__istartswith" % field: value}) - elif endswith: - retval = Q(**{"%s__iendswith" % field: value}) - else: # default - retval = Q(**{"%s__icontains" % field: value}) - return retval - -def build_person_query(request, search): - """ - Build and return a Django QuerySet and sort order for the Person - table. - """ - protect = not request.user.is_authenticated() - ### Build the order: - if protect: - # Do this to get the names sorted by private/alive - # NOTE: names can be private - terms = ["surname", "given", "id", "tag"] - query = Q(private=False) & Q(person__private=False) - order = ["surname__surname", "private", "person__probably_alive", - "first_name"] - else: - terms = ["surname", "given", "id", "tag", "public", "private"] - query = Q() - order = ["surname__surname", "first_name"] - ### Build the query: - if search: - if "[" in search: # "Surname, Given [I0002]" to match Flexbox and obj.get_select_string() - search = search.replace("[", ", id=^") - search = search.replace("]", "$") - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "surname": - query &= build_string_query("surname__surname", value, exact, startswith, endswith) - elif field == "given": - if protect: - query &= build_string_query("first_name", value, exact, startswith, endswith) & Q(person__probably_alive=False) - else: - query &= build_string_query("first_name", value, exact, startswith, endswith) - elif field == "private": - if not protect: - query &= Q(person__private=boolean(value)) - elif field == "public": - if not protect: - query &= Q(person__public=boolean(value)) - elif field == "birth": - if protect: - query &= Q(person__birth__year1=safe_int(value)) & Q(person__probably_alive=False) - else: - query &= Q(person__birth__year1=safe_int(value)) - elif field == "death": - if protect: - query &= Q(person__death__year1=safe_int(value)) & Q(person__probably_alive=False) - else: - query &= Q(person__death__year1=safe_int(value)) - elif field == "id": - query &= build_string_query("person__gramps_id", value, exact, startswith, endswith) - elif field == "gender": - query &= Q(person__gender_type__name=value.title()) - elif field == "tag": - query &= build_string_query("person__tags__name", value, exact, startswith, endswith) - else: - make_message(request, "Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(surname__surname__icontains=search) | - Q(surname__prefix__icontains=search) | - Q(person__gramps_id__icontains=search)) - else: - query &= (Q(surname__surname__icontains=search) | - Q(first_name__icontains=search) | - Q(suffix__icontains=search) | - Q(surname__prefix__icontains=search) | - Q(title__icontains=search) | - Q(person__gramps_id__icontains=search)) - else: # no search - pass # nothing else to do - #make_message(request, query) - return query, order, terms - -def build_family_query(request, search): - """ - Build and return a Django QuerySet and sort order for the Family - table. - """ - protect = not request.user.is_authenticated() - if protect: - terms = ["father", "mother", "id", "type", "surnames", "tag"] - query = (Q(private=False) & Q(father__private=False) & - Q(mother__private=False)) - order = ["father__name__surname__surname", - "father__private", "father__probably_alive", - "father__name__first_name", - "mother__name__surname__surname", - "mother__private", "mother__probably_alive", - "mother__name__first_name"] - else: - terms = ["father", "mother", "id", "type", "surnames", "father.name.first_name", - "mother.name.first_name", "tag", "public", "private"] - query = Q() - order = ["father__name__surname__surname", - "father__name__first_name", - "mother__name__surname__surname", - "mother__name__first_name"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - make_message("Ignoring value without specified field") - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "surnames": - query &= (build_string_query("father__name__surname__surname", value, exact, startswith, endswith) | - build_string_query("mother__name__surname__surname", value, exact, startswith, endswith)) - elif field == "father": - query &= build_string_query("father__name__surname__surname", value, exact, startswith, endswith) - elif field == "mother": - query &= build_string_query("mother__name__surname__surname", value, exact, startswith, endswith) - elif field == "type": - query &= build_string_query("family_rel_type__name", value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "tag": - query &= build_string_query("tags__name", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - make_message(request, message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: # need to protect! - query &= (Q(gramps_id__icontains=search) | - Q(family_rel_type__name__icontains=search) | - Q(father__name__surname__surname__icontains=search) | - Q(father__name__first_name__icontains=search) | - Q(mother__name__surname__surname__icontains=search) | - Q(mother__name__first_name__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search) | - Q(family_rel_type__name__icontains=search) | - Q(father__name__surname__surname__icontains=search) | - Q(father__name__first_name__icontains=search) | - Q(mother__name__surname__surname__icontains=search) | - Q(mother__name__first_name__icontains=search)) - else: # no search - pass # nothing left to do - #make_message(request, query) - return query, order, terms - -def build_media_query(request, search): - terms = ["id", "path", "description", "mime", "tag", "public", "private"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "path": - query &= build_string_query("path", value, exact, startswith, endswith) - elif field == "description": - query &= build_string_query("desc", value, exact, startswith, endswith) - elif field == "mime": - query &= build_string_query("mime", value, exact, startswith, endswith) - elif field == "tag": - query &= build_string_query("tags__name", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search) | - Q(path__icontains=search) | - Q(desc__icontains=search) | - Q(mime__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search) | - Q(path__icontains=search) | - Q(desc__icontains=search) | - Q(mime__icontains=search)) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_note_query(request, search): - terms = ["id", "type", "text", "tag", "public", "private"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "type": - query &= build_string_query("note_type__name", value, exact, startswith, endswith) - elif field == "text": - query &= build_string_query("text", value, exact, startswith, endswith) - elif field == "tag": - query &= build_string_query("tags__name", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search) | - Q(note_type__name__icontains=search) | - Q(text__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search) | - Q(note_type__name__icontains=search) | - Q(text__icontains=search)) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_place_query(request, search): - terms = ["title", "id", "public", "private"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "[" in search: # "Place [I0002]" to match Flexbox and obj.get_select_string() - search = search.replace("[", "; id=^") - search = search.replace("]", "$") - if ";" in search or "=" in search: - for term in [term.strip() for term in search.split(";")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "title": - query &= build_string_query("title", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search) | - Q(title__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search) | - Q(title__icontains=search)) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_repository_query(request, search): - terms = ["id", "name", "type", "public", "private"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "name": - query &= build_string_query("name", value, exact, startswith, endswith) - elif field == "type": - query &= build_string_query("repository_type__name", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search) | - Q(name__icontains=search) | - Q(repository_type__name__icontains=search) - ) - else: - query &= (Q(gramps_id__icontains=search) | - Q(name__icontains=search) | - Q(repository_type__name__icontains=search) - ) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_citation_query(request, search): - terms = ["id", "private", "public"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search)) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_source_query(request, search): - terms = ["id", "private", "public"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= Q(gramps_id__icontains=search) - else: - query &= Q(gramps_id__icontains=search) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_tag_query(request, search): - terms = ["name"] - protect = not request.user.is_authenticated() - if protect: - query = Q() # general privacy - order = ["name"] - else: - query = Q() - order = ["name"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "name": - query &= Q(name__icontains=value) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= Q(name__icontains=search) - else: - query &= Q(name__icontains=search) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_report_query(request, search): - terms = ["name"] - # NOTE: protection is based on super_user status - protect = not request.user.is_superuser - if protect: - query = ~Q(report_type="import") # general privacy - order = ["name"] - else: - query = Q() - order = ["name"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "name": - query &= Q(name__icontains=value) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= Q(name__icontains=search) - else: - query &= Q(name__icontains=search) - else: # no search - pass # nothing left to do - return query, order, terms - -def build_event_query(request, search): - terms = ["id", "type", "place", "description", "private", "public"] - protect = not request.user.is_authenticated() - if protect: - query = Q(private=False) # general privacy - order = ["gramps_id"] - else: - query = Q() - order = ["gramps_id"] - if search: - if "," in search or "=" in search: - for term in [term.strip() for term in search.split(",")]: - startswith = False - endswith = False - exact = False - if "=" in term: - field, value = [s.strip() for s in term.split("=")] - else: - if terms: - field = terms.pop(0) - value = term - else: - continue - if value.startswith("^"): - startswith = True - value = value[1:] - if value.endswith("$"): - endswith = True - value = value[:-1] - if startswith and endswith: - exact = True - if "." in field and not protect: - query &= build_string_query(field.replace(".", "__"), value, exact, startswith, endswith) - elif field == "id": - query &= build_string_query("gramps_id", value, exact, startswith, endswith) - elif field == "description": - query &= build_string_query("description", value, exact, startswith, endswith) - elif field == "type": - query &= build_string_query("event_type__name", value, exact, startswith, endswith) - elif field == "place": - query &= build_string_query("place__title", value, exact, startswith, endswith) - elif field == "private": - query &= Q(private=boolean(value)) - elif field == "public": - query &= Q(public=boolean(value)) - else: - request.user.message_set.create(message="Invalid query field '%s'" % field) - else: # no search fields, just raw search - if protect: - query &= (Q(gramps_id__icontains=search) | - Q(description__icontains=search) | - Q(event_type__name__icontains=search) | - Q(place__title__icontains=search)) - else: - query &= (Q(gramps_id__icontains=search) | - Q(description__icontains=search) | - Q(event_type__name__icontains=search) | - Q(place__title__icontains=search)) - else: # no search - pass # nothing left to do - return query, order, terms - -def safe_int(num): - """ - Safely try to convert num to an integer. Return -1 (which should - not match). - """ - try: - return int(num) - except: - return -1 - -def process_reference(request, ref_by, handle, ref_to, order): - # FIXME: can I make this work for all? - context = RequestContext(request) - ref_by_class = dji.get_model(ref_by) - referenced_by = ref_by_class.objects.get(handle=handle) - object_type = ContentType.objects.get_for_model(referenced_by) - ref_to_class = dji.get_model("%sRef" % ref_to.title()) - exclude = ["last_changed_by", "last_changed", "object_type", "object_id", "ref_object"] - if order == "new": - referenced_to = ref_to_class.objects.filter(object_id=referenced_by.id, - object_type=object_type, - order=0) - form = modelformset_factory(ref_to_class, exclude=exclude, extra=1)(queryset=referenced_to) - else: - referenced_to = ref_to_class.objects.filter(object_id=referenced_by.id, - object_type=object_type, - order=order) - form = modelformset_factory(ref_to_class, exclude=exclude, extra=0)(queryset=referenced_to) - form.model = referenced_to[0] - context["form"] = form - context["view"] = 'reference' - context["tview"] = _('Reference') - context["tviews"] = _('References') - context["object"] = referenced_by - context["handle"] = referenced_by.handle - context["url"] = referenced_to[0].get_reference_to().get_url() - #"/%s/%s" % (referenced_to[0].ref_object.__class__.__name__.lower(), - # referenced_to[0].ref_object.handle) - context["referenced_by"] = "/%s/%s" % (referenced_by.__class__.__name__.lower(), - referenced_by.handle) - context["action"] = "view" - return render_to_response("reference.html", context) - -def process_child(request, handle, act, child): - """ - handle - Family handle - act - 'remove', 'up', or 'down' - child - child number - """ - family = Family.objects.get(handle=handle) - obj_type = ContentType.objects.get_for_model(family) - childrefs = dji.ChildRef.filter(object_id=family.id, - object_type=obj_type).order_by("order") - - # FIXME: what about parent_families, families? - if act == "remove": - person = childrefs[int(child) - 1].ref_object - [f.delete() for f in person.parent_families.filter(handle=handle)] - childrefs[int(child) - 1].delete() - dji.rebuild_cache(person) - dji.rebuild_cache(family) - # FIXME: renumber order after delete - elif act == "up": - if int(child) >= 2: - for ref in childrefs: - if ref.order == int(child): - ref.order = ref.order - 1 - elif ref.order == int(child) - 1: - ref.order = ref.order + 1 - else: - ref.order = ref.order - for ref in childrefs: - ref.save() - dji.rebuild_cache(family) - elif act == "down": - if int(child) <= len(childrefs) - 1: - childrefs[int(child) - 1].order = int(child) + 1 - childrefs[int(child)].order = int(child) - childrefs[int(child) - 1].save() - childrefs[int(child)].save() - dji.rebuild_cache(family) - else: - raise Exception("invalid child action: %s" % act) - return redirect("/family/%s/" % handle) - -def process_list_item(request, view, handle, act, item, index): - # /person/872323636232635/remove/event/1 - # /family/872323636232635/up/citation/1 - # /citation/872323636232635/down/attribute/2 - index = int(index) - tab = { - "eventref": "#tab-events", - "citationref": "#tab-citations", - "repositoryref": "#tab-repositories", - "noteref": "#tab-notes", - "attribute": "#tab-attributes", - "media": "#tab-media", - "lds": "#tab-lds", - "parentfamily": "#tab-references", - "family": "#tab-references", - } - if view == "person": - obj = dji.Person.get(handle=handle) - elif view == "event": - obj = dji.Event.get(handle=handle) - elif view == "family": - obj = dji.Family.get(handle=handle) - elif view == "citation": - obj = dji.Citation.get(handle=handle) - elif view == "source": - obj = dji.Source.get(handle=handle) - else: - raise Exception("add '%s' to list" % view) - obj_type = ContentType.objects.get_for_model(obj) - # Next, get reference - if item == "eventref": - refs = dji.EventRef.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - elif item == "citationref": - refs = dji.CitationRef.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - elif item == "repositoryref": - refs = dji.RepositoryRef.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - elif item == "noteref": - refs = dji.NoteRef.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - elif item == "parentfamily": - refs = dji.MyParentFamilies.filter(person=obj).order_by("order") - elif item == "family": - refs = dji.MyFamilies.filter(person=obj).order_by("order") - else: - raise Exception("add '%s' to reference list" % item) - # Next, perform action: - if act == "remove": - count = 1 - done = False - for ref in refs: - if count == index and not done: - ref.delete() - done = True - else: - ref.order = count - ref.save() - count += 1 - elif act == "up" and index >= 2: - count = 1 - for ref in refs: - if count == index - 1: - ref.order = index - ref.save() - elif count == index: - ref.order = index - 1 - ref.save() - count += 1 - elif act == "down" and index < len(refs): - count = 1 - for ref in refs: - if count == index: - ref.order = index + 1 - ref.save() - elif count == index + 1: - ref.order = index - ref.save() - count += 1 - dji.rebuild_cache(obj) - return redirect("/%s/%s/%s" % (view, handle, tab[item])) - -def process_json_request(request): - """ - Process an Ajax/Json query request. - """ - import gramps.gen.lib - from gramps.gen.proxy import PrivateProxyDb, LivingProxyDb - if not request.user.is_authenticated(): - db = PrivateProxyDb(db) - db = LivingProxyDb(db, - LivingProxyDb.MODE_INCLUDE_LAST_NAME_ONLY, - None, # current year - 1) # years after death - field = request.GET.get("field", None) - query = request.GET.get("q", "") - page = int(request.GET.get("p", "1")) - size = int(request.GET.get("s", "10")) - if field == "mother": - q, order, terms = build_person_query(request, query) - q &= Q(person__gender_type__name="Female") - matches = Name.objects.filter(q).order_by(*order) - class_type = gramps.gen.lib.Person - handle_expr = "match.person.handle" - elif field == "father": - q, order, terms = build_person_query(request, query) - q &= Q(person__gender_type__name="Male") - matches = Name.objects.filter(q).order_by(*order) - class_type = gramps.gen.lib.Person - handle_expr = "match.person.handle" - elif field == "person": - q, order, terms = build_person_query(request, query) - matches = Name.objects.filter(q).order_by(*order) - class_type = gramps.gen.lib.Person - handle_expr = "match.person.handle" - elif field == "place": - q, order, terms = build_place_query(request, query) - matches = Place.objects.filter(q).order_by(*order) - class_type = gramps.gen.lib.Place - handle_expr = "match.handle" - else: - raise Exception("""Invalid field: '%s'; Example: /json/?field=mother&q=Smith&p=1&size=10""" % field) - ## ------------ - response_data = {"results": [], "total": len(matches)} - for match in matches[(page - 1) * size:page * size]: - obj = db.get_from_name_and_handle(class_type.__name__, eval(handle_expr)) - if obj: - response_data["results"].append(obj.to_struct()) - return HttpResponse(simplejson.dumps(response_data), content_type="application/json") diff --git a/gramps/webapp/init.py b/gramps/webapp/init.py deleted file mode 100644 index c416266fa..000000000 --- a/gramps/webapp/init.py +++ /dev/null @@ -1,219 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009-2012 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" -Creates a JSON representation of data for Django's fixture -architecture. We could have done this in Python, or SQL, -but this makes it useful for all Django-based backends -but still puts it into their syncdb API. -""" -import time -import os -os.environ["DJANGO_SETTINGS_MODULE"] = "default_settings" -import default_settings - -from gramps.gen.config import config -from gramps.gen.lib.nametype import NameType -from gramps.gen.lib.nameorigintype import NameOriginType -from gramps.gen.lib.attrtype import AttributeType -from gramps.gen.lib.urltype import UrlType -from gramps.gen.lib.childreftype import ChildRefType -from gramps.gen.lib.repotype import RepositoryType -from gramps.gen.lib.placetype import PlaceType -from gramps.gen.lib.eventtype import EventType -from gramps.gen.lib.familyreltype import FamilyRelType -from gramps.gen.lib.srcmediatype import SourceMediaType -from gramps.gen.lib.eventroletype import EventRoleType -from gramps.gen.lib.notetype import NoteType -from gramps.gen.lib.styledtexttagtype import StyledTextTagType - -from gramps.webapp.grampsdb.models import (GenderType, LdsType, LdsStatus, - NameFormatType, NameOriginType, - ThemeType) - -def get_datamap(x): - """ - Returns (code, Name) for a Gramps type tuple. - """ - return (x[0],x[2]) - -print("[") -for table, entries in [("grampsdb.config", - [(("setting", "\"sitename\""), - ("description", "\"site name of family tree\""), - ("value_type", "\"str\""), - ("value", "\"Gramps-Connect\"")), - (("setting", "\"db_version\""), - ("description", "\"database scheme version\""), - ("value_type", "\"str\""), - ("value", "\"0.6.1\"")), - (("setting", "\"db_created\""), - ("description", "\"database creation date/time\""), - ("value_type", "\"str\""), - ("value", ('"%s"' % time.strftime("%Y-%m-%d %H:%M")))), - ]), - ("grampsdb.report", - [(("name", '"Ahnentafel Report"'), - ('gramps_id', '"R0001"'), - ("handle", '"ancestor_report"'), - ("report_type", '"report"')), - (("name", '"birthday_report"'), - ('gramps_id', '"R0002"'), - ("handle", '"birthday_report"'), - ("report_type", '"report"')), - (("name", '"custom_text"'), - ('gramps_id', '"R0003"'), - ("handle", '"custom_text"'), - ("report_type", '"report"')), - (("name", '"descend_report"'), - ('gramps_id', '"R0004"'), - ("handle", '"descend_report"'), - ("report_type", '"report"')), - (("name", '"det_ancestor_report"'), - ('gramps_id', '"R0005"'), - ("handle", '"det_ancestor_report"'), - ("report_type", '"report"')), - (("name", '"det_descendant_report"'), - ('gramps_id', '"R0006"'), - ("handle", '"det_descendant_report"'), - ("report_type", '"report"')), - (("name", '"endofline_report"'), - ('gramps_id', '"R0007"'), - ("handle", '"endofline_report"'), - ("report_type", '"report"')), - (("name", '"family_group"'), - ('gramps_id', '"R0008"'), - ("handle", '"family_group"'), - ("report_type", '"report"')), - (("name", '"indiv_complete"'), - ('gramps_id', '"R0009"'), - ("handle", '"indiv_complete"'), - ("report_type", '"report"')), - (("name", '"kinship_report"'), - ('gramps_id', '"R0010"'), - ("handle", '"kinship_report"'), - ("report_type", '"report"')), - (("name", '"tag_report"'), - ('gramps_id', '"R0011"'), - ("handle", '"tag_report"'), - ("report_type", '"report"')), - (("name", '"number_of_ancestors"'), - ('gramps_id', '"R0012"'), - ("handle", '"number_of_ancestors"'), - ("report_type", '"report"')), - (("name", '"place_report"'), - ('gramps_id', '"R0013"'), - ("handle", '"place_report"'), - ("report_type", '"report"')), - (("name", '"simple_book_title"'), - ('gramps_id', '"R0014"'), - ("handle", '"simple_book_title"'), - ("report_type", '"report"')), - (("name", '"summary"'), - ('gramps_id', '"R0015"'), - ("handle", '"summary"'), - ("report_type", '"report"')), - (("name", '"Export"'), - ('gramps_id', '"R0016"'), - ("handle", '"gedcom_export"'), - ("options", '"off=ged"'), - ("report_type", '"export"')), - (("name", '"Gramps XML Export"'), - ('gramps_id', '"R0017"'), - ("handle", '"ex_gpkg"'), - ("options", '"off=gramps"'), - ("report_type", '"export"')), - (("name", '"Import"'), - ('gramps_id', '"R0018"'), - ("handle", '"im_ged"'), - ("options", '"iff=ged\\ni=http://arborvita.free.fr/Kennedy/Kennedy.ged"'), - ("report_type", '"import"')), - (("name", '"Gramps package (portable XML) Import"'), - ('gramps_id', '"R0019"'), - ("handle", '"im_gpkg"'), - ("options", '"iff=gramps\\ni=https://raw.githubusercontent.com/gramps-project/gramps/master/example/gramps/example.gramps"'), - ("report_type", '"import"')), - ])]: - entry_count = 0 - for entry in entries: - print(" {") - print(" \"model\": \"%s\"," % table) - print(" \"pk\": %d," % (entry_count + 1)) - print(" \"fields\":") - print(" {") - key_count = 0 - for items in entry: - key, value = items - print((" \"%s\" : %s" % (key, value)), end=' ') - key_count += 1 - if key_count < len(entry): - print(",") - else: - print() - print(" }") - print(" },") - entry_count += 1 - -pk = 4 -for section in config.get_sections(): - for setting in config.get_section_settings(section): - key = "%s.%s" % (section, setting) - value = config.get_default(key) - print(" {") - print(" \"model\": \"grampsdb.config\",") - print(" \"pk\": %d," % pk) - print(" \"fields\":") - print(" {") - print(" \"setting\" : \"%s\"," % key) - print(" \"value_type\" : \"%s\"," % type(value).__name__) - print(" \"value\": \"%s\"" % value) - print(" }") - print(" },", end=' ') - pk += 1 - -## Add the data for the type models: - -type_models = [NameType, NameOriginType, AttributeType, UrlType, ChildRefType, - RepositoryType, EventType, FamilyRelType, SourceMediaType, - EventRoleType, NoteType, GenderType, LdsType, LdsStatus, - NameFormatType, StyledTextTagType, ThemeType, PlaceType] -for type in type_models: - count = 1 - # Add each code: - for tuple in type._DATAMAP: - if len(tuple) == 3: # Gramps BSDDB style - val, name = get_datamap(tuple) - else: # NEW SQL based - val, name = tuple - print(" {") - print(" \"model\": \"grampsdb.%s\"," % type.__name__.lower()) - print(" \"pk\": %d," % count) - print(" \"fields\":") - print(" {") - print(" \"val\" : %d," % val) - print(" \"name\": \"%s\"" % name) - print(" }") - print(" }", end=' ') - # if it is the last one of the last one, no comma - if type == type_models[-1] and count == len(type._DATAMAP): - print() - else: - print(",") - count += 1 -print("]") diff --git a/gramps/webapp/init_gramps.py b/gramps/webapp/init_gramps.py deleted file mode 100644 index b1780c176..000000000 --- a/gramps/webapp/init_gramps.py +++ /dev/null @@ -1,30 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" -Clears gramps data -""" - -import os -os.environ["DJANGO_SETTINGS_MODULE"] = "settings" -import settings - -from .grampsdb.models import models as dj - -dj.clear_tables("primary", "secondary", "ref", "system") diff --git a/gramps/webapp/libdjango.py b/gramps/webapp/libdjango.py deleted file mode 100644 index 04faebdbb..000000000 --- a/gramps/webapp/libdjango.py +++ /dev/null @@ -1,2074 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Interface to Django models """ - -#------------------------------------------------------------------------ -# -# Python Modules -# -#------------------------------------------------------------------------ -import time -import sys -import pickle -import base64 -import collections - -#------------------------------------------------------------------------ -# -# Django Modules -# -#------------------------------------------------------------------------ -from django.contrib.contenttypes.models import ContentType -from django.db import transaction - -#------------------------------------------------------------------------ -# -# Gramps Modules -# -#------------------------------------------------------------------------ -import gramps.webapp.grampsdb.models as models -from gramps.gen.lib import Name -from gramps.gen.utils.id import create_id - -# To get a django person from a django database: -# djperson = dji.Person.get(handle='djhgsdh324hjg234hj24') -# -# To turn the djperson into a Gramps Person: -# tuple = dji.get_person(djperson) -# gperson = lib.gen.Person(tuple) -# OR -# gperson = djangodb.DbDjango().get_person_from_handle(handle) - -def check_diff(item, raw): - encoded = str(base64.encodebytes(pickle.dumps(raw)), "utf-8") - if item.cache != encoded: - print("Different:", item.__class__.__name__, item.gramps_id) - print("raw :", raw) - print("cache:", item.from_cache()) - # FIXING, TOO: - item.save_cache() - -#------------------------------------------------------------------------- -# -# Import functions -# -#------------------------------------------------------------------------- -def lookup_role_index(role0, event_ref_list): - """ - Find the handle in a unserialized event_ref_list and return code. - """ - if role0 is None: - return -1 - else: - count = 0 - for event_ref in event_ref_list: - (private, note_list, attribute_list, ref, erole) = event_ref - try: - event = models.Event.objects.get(handle=ref) - except: - return -1 - if event.event_type[0] == role0: - return count - count += 1 - return -1 - -def totime(dtime): - if dtime: - return int(time.mktime(dtime.timetuple())) - else: - return 0 - -#------------------------------------------------------------------------- -# -# Export functions -# -#------------------------------------------------------------------------- -def todate(t): - return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t)) - -def lookup(index, event_ref_list): - """ - Get the unserialized event_ref in an list of them and return it. - """ - if index < 0: - return None - else: - count = 0 - for event_ref in event_ref_list: - (private, note_list, attribute_list, ref, role) = event_ref - if index == count: - return ref - count += 1 - return None - -def get_datamap(grampsclass): - return [x[0] for x in grampsclass._DATAMAP if x[0] != grampsclass.CUSTOM] - -#------------------------------------------------------------------------- -# -# Django Interface -# -#------------------------------------------------------------------------- -class DjangoInterface(object): - """ - DjangoInterface for interoperating between Gramps and Django. - - This interface comes in a number of parts: - get_ITEMS() - add_ITEMS() - - get_ITEM(ITEM) - - Given an ITEM from a Django table, construct a Gramps Raw Data tuple. - - add_ITEM(data) - - Given a Gramps Raw Data tuple, add the data to the Django tables. - - - """ - def __init__(self): - self.debug = 0 - - def __getattr__(self, name): - """ - Django Objects database interface. - - >>> self.Person.all() - >>> self.Person.get(id=1) - >>> self.Person.get(handle='gh71234dhf3746347734') - """ - if hasattr(models, name): - return getattr(models, name).objects - else: - raise AttributeError("no such model: '%s'" % name) - - def get_next_id(self, obj, prefix): - """ - Get next gramps_id - - >>> dji.get_next_id(Person, "P") - 'P0002' - >>> dji.get_next_id(Media, "M") - 'M2349' - """ - ids = [o["gramps_id"] for o in obj.objects.values("gramps_id")] - count = 1 - while "%s%04d" % (prefix, count) in ids: - count += 1 - return "%s%04d" % (prefix, count) - - def get_model(self, name): - if hasattr(models, name): - return getattr(models, name) - elif hasattr(models, name.title()): - return getattr(models, name.title()) - else: - raise AttributeError("no such model: '%s'" % name) - - # ----------------------------------------------- - # Get methods to retrieve list data from the tables - # ----------------------------------------------- - - def clear_tables(self, *args): - return models.clear_tables(*args) - - def get_tag_list(self, obj): - return obj.get_tag_list() - - def get_attribute_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - attribute_list = models.Attribute.objects.filter(object_id=obj.id, - object_type=obj_type) - return list(map(self.pack_attribute, attribute_list)) - - def get_primary_name(self, person): - names = person.name_set.filter(preferred=True).order_by("order") - if len(names) > 0: - return Name.create(self.pack_name(names[0])) - else: - return Name() - - def get_alternate_names(self, person): - names = person.name_set.filter(preferred=False).order_by("order") - return [Name.create(self.pack_name(n)) for n in names] - - def get_names(self, person, preferred): - names = person.name_set.filter(preferred=preferred).order_by("order") - if preferred: - if len(names) > 0: - return self.pack_name(names[0]) - else: - return Name().serialize() - else: - return list(map(self.pack_name, names)) - - def get_source_attribute_list(self, source): - return [(map.private, map.key, map.value) for map in source.sourceattribute_set.all().order_by("order")] - - def get_citation_attribute_list(self, citation): - return [(map.private, map.key, map.value) for map in citation.citationattribute_set.all().order_by("order")] - - def get_media_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - mediarefs = models.MediaRef.objects.filter(object_id=obj.id, - object_type=obj_type) - return list(map(self.pack_media_ref, mediarefs)) - - def get_note_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - noterefs = models.NoteRef.objects.filter(object_id=obj.id, - object_type=obj_type) - return [noteref.ref_object.handle for noteref in noterefs] - - def get_repository_ref_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - reporefs = models.RepositoryRef.objects.filter(object_id=obj.id, - object_type=obj_type) - return list(map(self.pack_repository_ref, reporefs)) - - def get_place_ref_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - refs = models.PlaceRef.objects.filter(object_id=obj.id, - object_type=obj_type) - return list(map(self.pack_place_ref, refs)) - - def get_url_list(self, obj): - return list(map(self.pack_url, obj.url_set.all().order_by("order"))) - - def get_address_list(self, obj, with_parish): # person or repository - addresses = obj.address_set.all().order_by("order") - return [self.pack_address(address, with_parish) - for address in addresses] - - def get_child_ref_list(self, family): - obj_type = ContentType.objects.get_for_model(family) - childrefs = models.ChildRef.objects.filter(object_id=family.id, - object_type=obj_type).order_by("order") - return list(map(self.pack_child_ref, childrefs)) - - def get_citation_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - citationrefs = models.CitationRef.objects.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - return [citationref.citation.handle for citationref in citationrefs] - - def get_event_refs(self, obj, order="order"): - obj_type = ContentType.objects.get_for_model(obj) - eventrefs = models.EventRef.objects.filter(object_id=obj.id, - object_type=obj_type).order_by(order) - return eventrefs - - def get_event_ref_list(self, obj): - obj_type = ContentType.objects.get_for_model(obj) - eventrefs = models.EventRef.objects.filter(object_id=obj.id, - object_type=obj_type).order_by("order") - return list(map(self.pack_event_ref, eventrefs)) - - def get_family_list(self, person): # person has families - return [fam.family.handle for fam in - models.MyFamilies.objects.filter(person=person).order_by("order")] - - def get_parent_family_list(self, person): # person's parents has families - return [fam.family.handle for fam in - models.MyParentFamilies.objects.filter(person=person).order_by("order")] - - def get_person_ref_list(self, person): - obj_type = ContentType.objects.get_for_model(person) - return list(map(self.pack_person_ref, - models.PersonRef.objects.filter(object_id=person.id, - object_type=obj_type))) - - def get_lds_list(self, obj): # person or family - return list(map(self.pack_lds, obj.lds_set.all().order_by("order"))) - - def get_place_handle(self, obj): # obj is event - if obj.place: - return obj.place.handle - return None - - ## Packers: - - def get_event(self, event): - handle = event.handle - gid = event.gramps_id - the_type = tuple(event.event_type) - description = event.description - change = totime(event.last_changed) - private = event.private - note_list = self.get_note_list(event) - citation_list = self.get_citation_list(event) - media_list = self.get_media_list(event) - attribute_list = self.get_attribute_list(event) - date = self.get_date(event) - place_handle = self.get_place_handle(event) - tag_list = self.get_tag_list(event) - return (str(handle), gid, the_type, date, description, place_handle, - citation_list, note_list, media_list, attribute_list, - change, tag_list, private) - - def get_note_markup(self, note): - retval = [] - markups = models.Markup.objects.filter(note=note).order_by("order") - for markup in markups: - if markup.string and markup.string.isdigit(): - value = int(markup.string) - else: - value = markup.string - start_stop_list = markup.start_stop_list - ss_list = eval(start_stop_list) - retval += [(tuple(markup.styled_text_tag_type), value, ss_list)] - return retval - - def get_tag(self, tag): - changed = totime(tag.last_changed) - return (str(tag.handle), - tag.name, - tag.color, - tag.priority, - changed) - - def get_note(self, note): - styled_text = [note.text, self.get_note_markup(note)] - changed = totime(note.last_changed) - tag_list = self.get_tag_list(note) - return (str(note.handle), - note.gramps_id, - styled_text, - note.preformatted, - tuple(note.note_type), - changed, - tag_list, - note.private) - - def get_family(self, family): - child_ref_list = self.get_child_ref_list(family) - event_ref_list = self.get_event_ref_list(family) - media_list = self.get_media_list(family) - attribute_list = self.get_attribute_list(family) - lds_seal_list = self.get_lds_list(family) - citation_list = self.get_citation_list(family) - note_list = self.get_note_list(family) - tag_list = self.get_tag_list(family) - if family.father: - father_handle = family.father.handle - else: - father_handle = None - if family.mother: - mother_handle = family.mother.handle - else: - mother_handle = None - return (str(family.handle), family.gramps_id, - father_handle, mother_handle, - child_ref_list, tuple(family.family_rel_type), - event_ref_list, media_list, - attribute_list, lds_seal_list, - citation_list, note_list, - totime(family.last_changed), - tag_list, - family.private) - - def get_repository(self, repository): - note_list = self.get_note_list(repository) - address_list = self.get_address_list(repository, with_parish=False) - url_list = self.get_url_list(repository) - tag_list = self.get_tag_list(repository) - return (str(repository.handle), - repository.gramps_id, - tuple(repository.repository_type), - repository.name, - note_list, - address_list, - url_list, - totime(repository.last_changed), - tag_list, - repository.private) - - def get_citation(self, citation): - note_list = self.get_note_list(citation) - media_list = self.get_media_list(citation) - attribute_list = self.get_citation_attribute_list(citation) - tag_list = self.get_tag_list(citation) - date = self.get_date(citation) - # I guess citations can have no source - if citation.source: - handle = citation.source.handle - else: - handle = None - return (str(citation.handle), - citation.gramps_id, - date, - citation.page, - citation.confidence, - handle, - note_list, - media_list, - attribute_list, - totime(citation.last_changed), - tag_list, - citation.private) - - def get_source(self, source): - note_list = self.get_note_list(source) - media_list = self.get_media_list(source) - attribute_list = self.get_source_attribute_list(source) - reporef_list = self.get_repository_ref_list(source) - tag_list = self.get_tag_list(source) - return (str(source.handle), - source.gramps_id, - source.title, - source.author, - source.pubinfo, - note_list, - media_list, - source.abbrev, - totime(source.last_changed), - attribute_list, - reporef_list, - tag_list, - source.private) - - def get_media(self, media): - attribute_list = self.get_attribute_list(media) - citation_list = self.get_citation_list(media) - note_list = self.get_note_list(media) - tag_list = self.get_tag_list(media) - date = self.get_date(media) - return (str(media.handle), - media.gramps_id, - media.path, - str(media.mime), - str(media.desc), - media.checksum, - attribute_list, - citation_list, - note_list, - totime(media.last_changed), - date, - tag_list, - media.private) - - def get_person(self, person): - primary_name = self.get_names(person, True) # one - alternate_names = self.get_names(person, False) # list - event_ref_list = self.get_event_ref_list(person) - family_list = self.get_family_list(person) - parent_family_list = self.get_parent_family_list(person) - media_list = self.get_media_list(person) - address_list = self.get_address_list(person, with_parish=False) - attribute_list = self.get_attribute_list(person) - url_list = self.get_url_list(person) - lds_ord_list = self.get_lds_list(person) - pcitation_list = self.get_citation_list(person) - pnote_list = self.get_note_list(person) - person_ref_list = self.get_person_ref_list(person) - # This looks up the events for the first EventType given: - death_ref_index = person.death_ref_index - birth_ref_index = person.birth_ref_index - tag_list = self.get_tag_list(person) - - return (str(person.handle), - person.gramps_id, - tuple(person.gender_type)[0], - primary_name, - alternate_names, - death_ref_index, - birth_ref_index, - event_ref_list, - family_list, - parent_family_list, - media_list, - address_list, - attribute_list, - url_list, - lds_ord_list, - pcitation_list, - pnote_list, - totime(person.last_changed), - tag_list, - person.private, - person_ref_list) - - def get_date(self, obj): - if ((obj.calendar == obj.modifier == obj.quality == obj.sortval == obj.newyear == 0) and - obj.text == "" and (not obj.slash1) and (not obj.slash2) and - (obj.day1 == obj.month1 == obj.year1 == 0) and - (obj.day2 == obj.month2 == obj.year2 == 0)): - return None - elif ((not obj.slash1) and (not obj.slash2) and - (obj.day2 == obj.month2 == obj.year2 == 0)): - dateval = (obj.day1, obj.month1, obj.year1, obj.slash1) - else: - dateval = (obj.day1, obj.month1, obj.year1, obj.slash1, - obj.day2, obj.month2, obj.year2, obj.slash2) - return (obj.calendar, obj.modifier, obj.quality, dateval, - obj.text, obj.sortval, obj.newyear) - - def get_placename(self, place): - placename_date = self.get_date(place) - placename_value = place.name - placename_lang = place.lang - return (placename_value, - placename_date, - placename_lang) - - def get_place(self, place): - locations = place.location_set.all().order_by("order") - alt_location_list = [self.pack_location(location, True) for location in locations] - url_list = self.get_url_list(place) - media_list = self.get_media_list(place) - citation_list = self.get_citation_list(place) - note_list = self.get_note_list(place) - tag_list = self.get_tag_list(place) - place_ref_list = self.get_place_ref_list(place) - placename = self.get_placename(place) - return (str(place.handle), - place.gramps_id, - place.title, - place.long, - place.lat, - place_ref_list, - placename, - [], ## FIXME: get_alt_names - tuple(place.place_type), - place.code, - alt_location_list, - url_list, - media_list, - citation_list, - note_list, - totime(place.last_changed), - tag_list, - place.private) - - # --------------------------------- - # Packers - # --------------------------------- - - ## The packers build GRAMPS raw unserialized data. - - ## Reference packers - - def pack_child_ref(self, child_ref): - citation_list = self.get_citation_list(child_ref) - note_list = self.get_note_list(child_ref) - return (child_ref.private, citation_list, note_list, child_ref.ref_object.handle, - tuple(child_ref.father_rel_type), tuple(child_ref.mother_rel_type)) - - def pack_person_ref(self, personref): - citation_list = self.get_citation_list(personref) - note_list = self.get_note_list(personref) - return (personref.private, - citation_list, - note_list, - personref.ref_object.handle, - personref.description) - - def pack_media_ref(self, media_ref): - citation_list = self.get_citation_list(media_ref) - note_list = self.get_note_list(media_ref) - attribute_list = self.get_attribute_list(media_ref) - if ((media_ref.x1 == media_ref.y1 == media_ref.x2 == media_ref.y2 == -1) or - (media_ref.x1 == media_ref.y1 == media_ref.x2 == media_ref.y2 == 0)): - role = None - else: - role = (media_ref.x1, media_ref.y1, media_ref.x2, media_ref.y2) - return (media_ref.private, citation_list, note_list, attribute_list, - media_ref.ref_object.handle, role) - - def pack_repository_ref(self, repo_ref): - note_list = self.get_note_list(repo_ref) - return (note_list, - repo_ref.ref_object.handle, - repo_ref.call_number, - tuple(repo_ref.source_media_type), - repo_ref.private) - - def pack_place_ref(self, place_ref): - date = self.get_date(place_ref) - return (place_ref.ref_object.handle, date) - - def pack_media_ref(self, media_ref): - note_list = self.get_note_list(media_ref) - attribute_list = self.get_attribute_list(media_ref) - citation_list = self.get_citation_list(media_ref) - return (media_ref.private, citation_list, note_list, attribute_list, - media_ref.ref_object.handle, (media_ref.x1, - media_ref.y1, - media_ref.x2, - media_ref.y2)) - - def pack_event_ref(self, event_ref): - note_list = self.get_note_list(event_ref) - attribute_list = self.get_attribute_list(event_ref) - return (event_ref.private, note_list, attribute_list, - event_ref.ref_object.handle, tuple(event_ref.role_type)) - - def pack_citation(self, citation): - handle = citation.handle - gid = citation.gramps_id - date = self.get_date(citation) - page = citation.page - confidence = citation.confidence - source_handle = citation.source.handle - note_list = self.get_note_list(citation) - media_list = self.get_media_list(citation) - attribute_list = self.get_citation_attribute_list(citation) - changed = totime(citation.last_changed) - private = citation.private - tag_list = self.get_tag_list(citation) - return (handle, gid, date, page, confidence, source_handle, - note_list, media_list, attribute_list, changed, tag_list, - private) - - def pack_address(self, address, with_parish): - citation_list = self.get_citation_list(address) - date = self.get_date(address) - note_list = self.get_note_list(address) - locations = address.location_set.all().order_by("order") - if len(locations) > 0: - location = self.pack_location(locations[0], with_parish) - else: - if with_parish: - location = (("", "", "", "", "", "", ""), "") - else: - location = ("", "", "", "", "", "", "") - return (address.private, citation_list, note_list, date, location) - - def pack_lds(self, lds): - citation_list = self.get_citation_list(lds) - note_list = self.get_note_list(lds) - date = self.get_date(lds) - if lds.famc: - famc = lds.famc.handle - else: - famc = None - place_handle = self.get_place_handle(lds) - return (citation_list, note_list, date, lds.lds_type[0], place_handle, - famc, lds.temple, lds.status[0], lds.private) - - def pack_source(self, source): - note_list = self.get_note_list(source) - media_list = self.get_media_list(source) - reporef_list = self.get_repository_ref_list(source) - attribute_list = self.get_source_attribute_list(source) - tag_list = self.get_tag_list(source) - return (source.handle, source.gramps_id, source.title, - source.author, source.pubinfo, - note_list, - media_list, - source.abbrev, - totime(last_changed), attribute_list, - reporef_list, - tag_list, - source.private) - - def pack_name(self, name): - citation_list = self.get_citation_list(name) - note_list = self.get_note_list(name) - date = self.get_date(name) - return (name.private, citation_list, note_list, date, - name.first_name, name.make_surname_list(), name.suffix, - name.title, tuple(name.name_type), - name.group_as, name.sort_as.val, - name.display_as.val, name.call, name.nick, - name.famnick) - - def pack_location(self, loc, with_parish): - if with_parish: - return ((loc.street, loc.locality, loc.city, loc.county, loc.state, loc.country, - loc.postal, loc.phone), loc.parish) - else: - return (loc.street, loc.locality, loc.city, loc.county, loc.state, loc.country, - loc.postal, loc.phone) - - def pack_url(self, url): - return (url.private, url.path, url.desc, tuple(url.url_type)) - - def pack_attribute(self, attribute): - citation_list = self.get_citation_list(attribute) - note_list = self.get_note_list(attribute) - return (attribute.private, - citation_list, - note_list, - tuple(attribute.attribute_type), - attribute.value) - - - ## Export lists: - - def add_child_ref_list(self, obj, ref_list): - ## Currently, only Family references children - for child_data in ref_list: - self.add_child_ref(obj, child_data) - - def add_citation_list(self, obj, citation_list): - for citation_handle in citation_list: - self.add_citation_ref(obj, citation_handle) - - def add_event_ref_list(self, obj, event_ref_list): - for event_ref in event_ref_list: - self.add_event_ref(obj, event_ref) - - def add_surname_list(self, name, surname_list): - order = 1 - for data in surname_list: - (surname_text, prefix, primary, origin_type, - connector) = data - surname = models.Surname() - surname.surname = surname_text - surname.prefix = prefix - surname.primary = primary - surname.name_origin_type = models.get_type(models.NameOriginType, - origin_type) - surname.connector = connector - surname.name = name - surname.order = order - surname.save() - order += 1 - - def add_note_list(self, obj, note_list): - for handle in note_list: - # Just the handle - try: - note = models.Note.objects.get(handle=handle) - self.add_note_ref(obj, note) - except: - print(("ERROR: Note does not exist: '%s'" % - str(handle)), file=sys.stderr) - - def add_alternate_name_list(self, person, alternate_names): - for name in alternate_names: - if name: - self.add_name(person, name, False) - - def add_parent_family_list(self, person, parent_family_list): - for parent_family_data in parent_family_list: - self.add_parent_family(person, parent_family_data) - - def add_media_ref_list(self, person, media_list): - for media_data in media_list: - self.add_media_ref(person, media_data) - - def add_attribute_list(self, obj, attribute_list): - for attribute_data in attribute_list: - self.add_attribute(obj, attribute_data) - - def add_tag_list(self, obj, tag_list): - for tag_handle in tag_list: - try: - tag = models.Tag.objects.get(handle=tag_handle) - except: - print(("ERROR: Tag does not exist: '%s'" % - str(tag_handle)), file=sys.stderr) - obj.tags.add(tag) - - def add_url_list(self, field, obj, url_list): - if not url_list: return None - count = 1 - for url_data in url_list: - self.add_url(field, obj, url_data, count) - count += 1 - - def add_person_ref_list(self, obj, person_ref_list): - for person_ref_data in person_ref_list: - self.add_person_ref(obj, person_ref_data) - - def add_address_list(self, field, obj, address_list): - count = 1 - for address_data in address_list: - self.add_address(field, obj, address_data, count) - count += 1 - - def add_lds_list(self, field, obj, lds_ord_list): - count = 1 - for ldsord in lds_ord_list: - lds = self.add_lds(field, obj, ldsord, count) - #obj.lds_list.add(lds) - #obj.save() - count += 1 - - def add_repository_ref_list(self, obj, reporef_list): - for data in reporef_list: - self.add_repository_ref(obj, data) - - def add_place_ref_list(self, obj, placeref_list): - for data in placeref_list: - self.add_place_ref(obj, data) - - def add_family_ref_list(self, person, family_list): - for family_handle in family_list: - self.add_family_ref(person, family_handle) - - def add_alt_name_list(self, place, alt_name_list): - print("FIXME: add alt_name_list!", alt_name_list) - - ## Export reference objects: - - def add_person_ref_default(self, obj, person, private=False, desc=None): - count = person.references.count() - person_ref = models.PersonRef(referenced_by=obj, - ref_object=person, - private=private, - order=count + 1, - description=desc) - person_ref.save() - - def add_person_ref(self, obj, person_ref_data): - (private, - citation_list, - note_list, - handle, - desc) = person_ref_data - try: - person = models.Person.objects.get(handle=handle) - except: - print(("ERROR: Person does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - - count = person.references.count() - person_ref = models.PersonRef(referenced_by=obj, - ref_object=person, - private=private, - order=count + 1, - description=desc) - person_ref.save() - self.add_note_list(person_ref, note_list) - self.add_citation_list(person_ref, citation_list) - - def add_note_ref(self, obj, note): - count = note.references.count() - note_ref = models.NoteRef(referenced_by=obj, - ref_object=note, - private=False, - order=count + 1) - note_ref.save() - - def add_media_ref_default(self, obj, media, private=False, role=None): - count = media.references.count() - if not role: - role = (0,0,0,0) - media_ref = models.MediaRef(referenced_by=obj, - ref_object=media, - x1=role[0], - y1=role[1], - x2=role[2], - y2=role[3], - private=private, - order=count + 1) - media_ref.save() - - def add_media_ref(self, obj, media_ref_data): - (private, citation_list, note_list, attribute_list, - ref, role) = media_ref_data - try: - media = models.Media.objects.get(handle=ref) - except: - print(("ERROR: Media does not exist: '%s'" % - str(ref)), file=sys.stderr) - return - count = media.references.count() - if not role: - role = (0,0,0,0) - media_ref = models.MediaRef(referenced_by=obj, - ref_object=media, - x1=role[0], - y1=role[1], - x2=role[2], - y2=role[3], - private=private, - order=count + 1) - media_ref.save() - self.add_note_list(media_ref, note_list) - self.add_attribute_list(media_ref, attribute_list) - self.add_citation_list(media_ref, citation_list) - - def add_citation_ref_default(self, obj, citation, private=False): - object_type = ContentType.objects.get_for_model(obj) - count = models.CitationRef.objects.filter(object_id=obj.id,object_type=object_type).count() - citation_ref = models.CitationRef(private=private, - referenced_by=obj, - citation=citation, - order=count + 1) - citation_ref.save() - - def add_citation_ref(self, obj, handle): - try: - citation = models.Citation.objects.get(handle=handle) - except: - print(("ERROR: Citation does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - - object_type = ContentType.objects.get_for_model(obj) - count = models.CitationRef.objects.filter(object_id=obj.id,object_type=object_type).count() - citation_ref = models.CitationRef(private=False, - referenced_by=obj, - citation=citation, - order=count + 1) - citation_ref.save() - - def add_citation(self, citation_data): - (handle, gid, date, page, confidence, source_handle, note_list, - media_list, attribute_list, changed, tag_list, private) = citation_data - citation = models.Citation( - handle=handle, - gramps_id=gid, - private=private, - last_changed=todate(changed), - confidence=confidence, - page=page) - citation.save(save_cache=False) - - def add_citation_detail(self, citation_data): - (handle, gid, date, page, confidence, source_handle, note_list, - media_list, attribute_list, change, tag_list, private) = citation_data - try: - citation = models.Citation.objects.get(handle=handle) - except: - print(("ERROR: Citation does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - try: - source = models.Source.objects.get(handle=source_handle) - except: - print(("ERROR: Source does not exist: '%s'" % - str(source_handle)), file=sys.stderr) - return - citation.source = source - self.add_date(citation, date) - citation.save(save_cache=False) - self.add_note_list(citation, note_list) - self.add_media_ref_list(citation, media_list) - self.add_citation_attribute_list(citation, attribute_list) - self.add_tag_list(citation, tag_list) - citation.save_cache() - - def add_child_ref_default(self, obj, child, frel=1, mrel=1, private=False): - object_type = ContentType.objects.get_for_model(obj) # obj is family - count = models.ChildRef.objects.filter(object_id=obj.id,object_type=object_type).count() - child_ref = models.ChildRef(private=private, - referenced_by=obj, - ref_object=child, - order=count + 1, - father_rel_type=models.get_type(models.ChildRefType, frel), # birth - mother_rel_type=models.get_type(models.ChildRefType, mrel)) - child_ref.save() - - def add_child_ref(self, obj, data): - (private, citation_list, note_list, ref, frel, mrel) = data - try: - child = models.Person.objects.get(handle=ref) - except: - print(("ERROR: Person does not exist: '%s'" % - str(ref)), file=sys.stderr) - return - object_type = ContentType.objects.get_for_model(obj) - count = models.ChildRef.objects.filter(object_id=obj.id,object_type=object_type).count() - child_ref = models.ChildRef(private=private, - referenced_by=obj, - ref_object=child, - order=count + 1, - father_rel_type=models.get_type(models.ChildRefType, frel), - mother_rel_type=models.get_type(models.ChildRefType, mrel)) - child_ref.save() - self.add_citation_list(child_ref, citation_list) - self.add_note_list(child_ref, note_list) - - def add_event_ref_default(self, obj, event, private=False, role=models.EventRoleType._DEFAULT): - object_type = ContentType.objects.get_for_model(obj) - count = models.EventRef.objects.filter(object_id=obj.id,object_type=object_type).count() - event_ref = models.EventRef(private=private, - referenced_by=obj, - ref_object=event, - order=count + 1, - role_type = models.get_type(models.EventRoleType, role)) - event_ref.save() - - def add_event_ref(self, obj, event_data): - (private, note_list, attribute_list, ref, role) = event_data - try: - event = models.Event.objects.get(handle=ref) - except: - print(("ERROR: Event does not exist: '%s'" % - str(ref)), file=sys.stderr) - return - object_type = ContentType.objects.get_for_model(obj) - count = models.EventRef.objects.filter(object_id=obj.id,object_type=object_type).count() - event_ref = models.EventRef(private=private, - referenced_by=obj, - ref_object=event, - order=count + 1, - role_type = models.get_type(models.EventRoleType, role)) - event_ref.save() - self.add_note_list(event_ref, note_list) - self.add_attribute_list(event_ref, attribute_list) - - def add_repository_ref_default(self, obj, repository, private=False, call_number="", - source_media_type=models.SourceMediaType._DEFAULT): - object_type = ContentType.objects.get_for_model(obj) - count = models.RepositoryRef.objects.filter(object_id=obj.id,object_type=object_type).count() - repos_ref = models.RepositoryRef(private=private, - referenced_by=obj, - call_number=call_number, - source_media_type=models.get_type(models.SourceMediaType, - source_media_type), - ref_object=repository, - order=count + 1) - repos_ref.save() - - def add_repository_ref(self, obj, reporef_data): - (note_list, - ref, - call_number, - source_media_type, - private) = reporef_data - try: - repository = models.Repository.objects.get(handle=ref) - except: - print(("ERROR: Repository does not exist: '%s'" % - str(ref)), file=sys.stderr) - return - object_type = ContentType.objects.get_for_model(obj) - count = models.RepositoryRef.objects.filter(object_id=obj.id,object_type=object_type).count() - repos_ref = models.RepositoryRef(private=private, - referenced_by=obj, - call_number=call_number, - source_media_type=models.get_type(models.SourceMediaType, - source_media_type), - ref_object=repository, - order=count + 1) - repos_ref.save() - self.add_note_list(repos_ref, note_list) - - def add_family_ref(self, obj, handle): - try: - family = models.Family.objects.get(handle=handle) - except: - print(("ERROR: Family does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - #obj.families.add(family) - pfo = models.MyFamilies(person=obj, family=family, - order=len(models.MyFamilies.objects.filter(person=obj)) + 1) - pfo.save() - obj.save() - - ## Export individual objects: - - def add_source_attribute_list(self, source, attribute_list): - ## FIXME: dict to list - count = 1 - #for key in datamap_dict: - # value = datamap_dict[key] - # datamap = models.SourceDatamap(key=key, value=value, order=count) - # datamap.source = source - # datamap.save() - # count += 1 - - def add_citation_attribute_list(self, citation, attribute_list): - ## FIXME: dict to list - count = 1 - #for key in datamap_dict: - # value = datamap_dict[key] - # datamap = models.CitationDatamap(key=key, value=value, order=count) - # datamap.citation = citation - # datamap.save() - # count += 1 - - def add_lds(self, field, obj, data, order): - (lcitation_list, lnote_list, date, type, place_handle, - famc_handle, temple, status, private) = data - if place_handle: - try: - place = models.Place.objects.get(handle=place_handle) - except: - print(("ERROR: Place does not exist: '%s'" % - str(place_handle)), file=sys.stderr) - place = None - else: - place = None - if famc_handle: - try: - famc = models.Family.objects.get(handle=famc_handle) - except: - print(("ERROR: Family does not exist: '%s'" % - str(famc_handle)), file=sys.stderr) - famc = None - else: - famc = None - lds = models.Lds(lds_type = models.get_type(models.LdsType, type), - temple=temple, - place=place, - famc=famc, - order=order, - status = models.get_type(models.LdsStatus, status), - private=private) - self.add_date(lds, date) - lds.save() - self.add_note_list(lds, lnote_list) - self.add_citation_list(lds, lcitation_list) - if field == "person": - lds.person = obj - elif field == "family": - lds.family = obj - else: - raise AttributeError("invalid field '%s' to attach lds" % - str(field)) - lds.save() - return lds - - def add_address(self, field, obj, address_data, order): - (private, acitation_list, anote_list, date, location) = address_data - address = models.Address(private=private, order=order) - self.add_date(address, date) - address.save() - self.add_location("address", address, location, 1) - self.add_note_list(address, anote_list) - self.add_citation_list(address, acitation_list) - if field == "person": - address.person = obj - elif field == "repository": - address.repository = obj - else: - raise AttributeError("invalid field '%s' to attach address" % - str(field)) - address.save() - #obj.save() - #obj.addresses.add(address) - #obj.save() - - def add_attribute(self, obj, attribute_data): - (private, citation_list, note_list, the_type, value) = attribute_data - attribute_type = models.get_type(models.AttributeType, the_type) - attribute = models.Attribute(private=private, - attribute_of=obj, - attribute_type=attribute_type, - value=value) - attribute.save() - self.add_citation_list(attribute, citation_list) - self.add_note_list(attribute, note_list) - #obj.attributes.add(attribute) - #obj.save() - - def add_url(self, field, obj, url_data, order): - (private, path, desc, type) = url_data - url = models.Url(private=private, - path=path, - desc=desc, - order=order, - url_type=models.get_type(models.UrlType, type)) - if field == "person": - url.person = obj - elif field == "repository": - url.repository = obj - elif field == "place": - url.place = obj - else: - raise AttributeError("invalid field '%s' to attach to url" % - str(field)) - url.save() - #obj.url_list.add(url) - #obj.save() - - def add_place_ref_default(self, obj, place, date=None): - count = place.references.count() - object_type = ContentType.objects.get_for_model(obj) - count = models.PlaceRef.objects.filter(object_id=obj.id, - object_type=object_type).count() - place_ref = models.PlaceRef(referenced_by=obj, - ref_object=place, - order=count + 1) - self.add_date(obj, date) - place_ref.save() - - def add_place_ref(self, obj, data): - place_handle, date = data - if place_handle: - try: - place = models.Place.objects.get(handle=place_handle) - except: - print(("ERROR: Place does not exist: '%s'" % str(place_handle)), file=sys.stderr) - #from gramps.gen.utils.debug import format_exception - #print("".join(format_exception()), file=sys.stderr) - return - object_type = ContentType.objects.get_for_model(obj) - count = models.PlaceRef.objects.filter(object_id=obj.id,object_type=object_type).count() - place_ref = models.PlaceRef(referenced_by=obj, ref_object=place, order=count + 1) - place_ref.save() - self.add_date(place_ref, date) - - def add_parent_family(self, person, parent_family_handle): - try: - family = models.Family.objects.get(handle=parent_family_handle) - except: - print(("ERROR: Family does not exist: '%s'" % - str(parent_family_handle)), file=sys.stderr) - return - #person.parent_families.add(family) - pfo = models.MyParentFamilies( - person=person, - family=family, - order=len(models.MyParentFamilies.objects.filter(person=person)) + 1) - pfo.save() - person.save() - - def add_date(self, obj, date): - if date is None: - (calendar, modifier, quality, text, sortval, newyear) = \ - (0, 0, 0, "", 0, 0) - day1, month1, year1, slash1 = 0, 0, 0, 0 - day2, month2, year2, slash2 = 0, 0, 0, 0 - else: - (calendar, modifier, quality, dateval, text, sortval, newyear) = date - if len(dateval) == 4: - day1, month1, year1, slash1 = dateval - day2, month2, year2, slash2 = 0, 0, 0, 0 - elif len(dateval) == 8: - day1, month1, year1, slash1, day2, month2, year2, slash2 = dateval - else: - raise AttributeError("ERROR: dateval format '%s'" % str(dateval)) - obj.calendar = calendar - obj.modifier = modifier - obj.quality = quality - obj.text = text - obj.sortval = sortval - obj.newyear = newyear - obj.day1 = day1 - obj.month1 = month1 - obj.year1 = year1 - obj.slash1 = slash1 - obj.day2 = day2 - obj.month2 = month2 - obj.year2 = year2 - obj.slash2 = slash2 - - def add_name(self, person, data, preferred): - if data: - (private, citation_list, note_list, date, - first_name, surname_list, suffix, title, - name_type, group_as, sort_as, - display_as, call, nick, famnick) = data - - count = person.name_set.count() - name = models.Name() - name.order = count + 1 - name.preferred = preferred - name.private = private - name.first_name = first_name - name.suffix = suffix - name.title = title - name.name_type = models.get_type(models.NameType, name_type) - name.group_as = group_as - name.sort_as = models.get_type(models.NameFormatType, sort_as) - name.display_as = models.get_type(models.NameFormatType, display_as) - name.call = call - name.nick = nick - name.famnick = famnick - # we know person exists - # needs to have an ID for key - name.person = person - self.add_date(name, date) - name.save() - self.add_surname_list(name, surname_list) - self.add_note_list(name, note_list) - self.add_citation_list(name, citation_list) - #person.save() - - ## Export primary objects: - - def add_person(self, data): - # Unpack from the BSDDB: - (handle, # 0 - gid, # 1 - gender, # 2 - primary_name, # 3 - alternate_names, # 4 - death_ref_index, # 5 - birth_ref_index, # 6 - event_ref_list, # 7 - family_list, # 8 - parent_family_list, # 9 - media_list, # 10 - address_list, # 11 - attribute_list, # 12 - url_list, # 13 - lds_ord_list, # 14 - pcitation_list, # 15 - pnote_list, # 16 - change, # 17 - tag_list, # 18 - private, # 19 - person_ref_list, # 20 - ) = data - - person = models.Person(handle=handle, - gramps_id=gid, - last_changed=todate(change), - private=private, - gender_type=models.get_type(models.GenderType, gender)) - person.save(save_cache=False) - - def add_person_detail(self, data): - # Unpack from the BSDDB: - (handle, # 0 - gid, # 1 - gender, # 2 - primary_name, # 3 - alternate_names, # 4 - death_ref_index, # 5 - birth_ref_index, # 6 - event_ref_list, # 7 - family_list, # 8 - parent_family_list, # 9 - media_list, # 10 - address_list, # 11 - attribute_list, # 12 - url_list, # 13 - lds_ord_list, # 14 - pcitation_list, # 15 - pnote_list, # 16 - change, # 17 - tag_list, # 18 - private, # 19 - person_ref_list, # 20 - ) = data - - try: - person = models.Person.objects.get(handle=handle) - except: - print(("ERROR: Person does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - if primary_name: - self.add_name(person, primary_name, True) - self.add_alternate_name_list(person, alternate_names) - self.add_event_ref_list(person, event_ref_list) - self.add_family_ref_list(person, family_list) - self.add_parent_family_list(person, parent_family_list) - self.add_media_ref_list(person, media_list) - self.add_note_list(person, pnote_list) - self.add_attribute_list(person, attribute_list) - self.add_url_list("person", person, url_list) - self.add_person_ref_list(person, person_ref_list) - self.add_citation_list(person, pcitation_list) - self.add_address_list("person", person, address_list) - self.add_lds_list("person", person, lds_ord_list) - self.add_tag_list(person, tag_list) - # set person.birth and birth.death to correct events: - - obj_type = ContentType.objects.get_for_model(person) - events = models.EventRef.objects.filter( - object_id=person.id, - object_type=obj_type, - ref_object__event_type__val=models.EventType.BIRTH).order_by("order") - - all_events = self.get_event_ref_list(person) - if events: - person.birth = events[0].ref_object - person.birth_ref_index = lookup_role_index(models.EventType.BIRTH, all_events) - - events = models.EventRef.objects.filter( - object_id=person.id, - object_type=obj_type, - ref_object__event_type__val=models.EventType.DEATH).order_by("order") - if events: - person.death = events[0].ref_object - person.death_ref_index = lookup_role_index(models.EventType.DEATH, all_events) - person.save() - return person - - def save_note_markup(self, note, markup_list): - # delete any prexisting markup: - models.Markup.objects.filter(note=note).delete() - count = 1 - for markup in markup_list: - markup_code, value, start_stop_list = markup - m = models.Markup( - note=note, - order=count, - styled_text_tag_type=models.get_type(models.StyledTextTagType, - markup_code, - get_or_create=False), - string=value, - start_stop_list=str(start_stop_list)) - m.save() - - def add_note(self, data): - # Unpack from the BSDDB: - (handle, gid, styled_text, format, note_type, - change, tag_list, private) = data - text, markup_list = styled_text - n = models.Note(handle=handle, - gramps_id=gid, - last_changed=todate(change), - private=private, - preformatted=format, - text=text, - note_type=models.get_type(models.NoteType, note_type)) - n.save(save_cache=False) - self.save_note_markup(n, markup_list) - - def add_note_detail(self, data): - # Unpack from the BSDDB: - (handle, gid, styled_text, format, note_type, - change, tag_list, private) = data - note = models.Note.objects.get(handle=handle) - note.save(save_cache=False) - self.add_tag_list(note, tag_list) - note.save_cache() - - def add_family(self, data): - # Unpack from the BSDDB: - (handle, gid, father_handle, mother_handle, - child_ref_list, the_type, event_ref_list, media_list, - attribute_list, lds_seal_list, citation_list, note_list, - change, tag_list, private) = data - - family = models.Family(handle=handle, gramps_id=gid, - family_rel_type = models.get_type(models.FamilyRelType, the_type), - last_changed=todate(change), - private=private) - family.save(save_cache=False) - - def add_family_detail(self, data): - # Unpack from the BSDDB: - (handle, gid, father_handle, mother_handle, - child_ref_list, the_type, event_ref_list, media_list, - attribute_list, lds_seal_list, citation_list, note_list, - change, tag_list, private) = data - - try: - family = models.Family.objects.get(handle=handle) - except: - print(("ERROR: Family does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - # father_handle and/or mother_handle can be None - if father_handle: - try: - family.father = models.Person.objects.get(handle=father_handle) - except: - print(("ERROR: Father does not exist: '%s'" % - str(father_handle)), file=sys.stderr) - family.father = None - if mother_handle: - try: - family.mother = models.Person.objects.get(handle=mother_handle) - except: - print(("ERROR: Mother does not exist: '%s'" % - str(mother_handle)), file=sys.stderr) - family.mother = None - family.save(save_cache=False) - self.add_child_ref_list(family, child_ref_list) - self.add_note_list(family, note_list) - self.add_attribute_list(family, attribute_list) - self.add_citation_list(family, citation_list) - self.add_media_ref_list(family, media_list) - self.add_event_ref_list(family, event_ref_list) - self.add_lds_list("family", family, lds_seal_list) - self.add_tag_list(family, tag_list) - family.save_cache() - - def add_source(self, data): - (handle, gid, title, - author, pubinfo, - note_list, - media_list, - abbrev, - change, attribute_list, - reporef_list, - tag_list, - private) = data - source = models.Source(handle=handle, gramps_id=gid, title=title, - author=author, pubinfo=pubinfo, abbrev=abbrev, - last_changed=todate(change), private=private) - source.save(save_cache=False) - - def add_source_detail(self, data): - (handle, gid, title, - author, pubinfo, - note_list, - media_list, - abbrev, - change, attribute_list, - reporef_list, - tag_list, - private) = data - try: - source = models.Source.objects.get(handle=handle) - except: - print(("ERROR: Source does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - source.save(save_cache=False) - self.add_note_list(source, note_list) - self.add_media_ref_list(source, media_list) - self.add_source_attribute_list(source, attribute_list) - self.add_repository_ref_list(source, reporef_list) - self.add_tag_list(source, tag_list) - source.save_cache() - - def add_repository(self, data): - (handle, gid, the_type, name, note_list, - address_list, url_list, change, tag_list, private) = data - - repository = models.Repository(handle=handle, - gramps_id=gid, - last_changed=todate(change), - private=private, - repository_type=models.get_type(models.RepositoryType, the_type), - name=name) - repository.save(save_cache=False) - - def add_repository_detail(self, data): - (handle, gid, the_type, name, note_list, - address_list, url_list, change, tag_list, private) = data - try: - repository = models.Repository.objects.get(handle=handle) - except: - print(("ERROR: Repository does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - repository.save(save_cache=False) - self.add_note_list(repository, note_list) - self.add_url_list("repository", repository, url_list) - self.add_address_list("repository", repository, address_list) - self.add_tag_list(repository, tag_list) - repository.save_cache() - - def add_location(self, field, obj, location_data, order): - # location now has 8 items - # street, locality, city, county, state, - # country, postal, phone, parish - - if location_data == None: return - if len(location_data) == 8: - (street, locality, city, county, state, country, postal, phone) = location_data - parish = None - elif len(location_data) == 2: - ((street, locality, city, county, state, country, postal, phone), parish) = location_data - else: - print(("ERROR: unknown location: '%s'" % - str(location_data)), file=sys.stderr) - (street, locality, city, county, state, country, postal, phone, parish) = \ - ("", "", "", "", "", "", "", "", "") - location = models.Location(street = street, - locality = locality, - city = city, - county = county, - state = state, - country = country, - postal = postal, - phone = phone, - parish = parish, - order = order) - if field == "address": - location.address = obj - elif field == "place": - location.place = obj - else: - raise AttributeError("invalid field '%s' to attach to location" % - str(field)) - location.save() - #obj.locations.add(location) - #obj.save() - - def add_place(self, data): - ## ('cef246c95c132bcf6a0255d4d17', 'P0036', ('Santa Clara Co., CA, USA', DATE, "English"), - ## '', '', [('cef243fb5634559442323368f63', None)], 'Santa Clara Co.', [], (3, ''), '', [], [], [], [], [], 1422124781, [], False) - (handle, gid, title, long, lat, - place_ref_list, - (placename, date, placelang), - alt_name_list, - place_type, - code, - alt_location_list, - url_list, - media_list, - citation_list, - note_list, - change, - tag_list, - private) = data - place = models.Place( - handle=handle, - gramps_id=gid, - title=title, - long=long, - lat=lat, - name=placename, - lang=placelang, - place_type=models.get_type(models.PlaceType, place_type), - code=code, - last_changed=todate(change), - private=private) - try: - place.save(save_cache=False) - except: - print("FIXME: error in saving place") - - def add_place_detail(self, data): - (handle, gid, title, long, lat, - place_ref_list, - (placename, date, placelang), - alt_name_list, - place_type, - code, - alt_location_list, - url_list, - media_list, - citation_list, - note_list, - change, - tag_list, - private) = data - try: - place = models.Place.objects.get(handle=handle) - except: - print(("ERROR: Place does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - self.add_date(place, date) - place.save(save_cache=False) - self.add_url_list("place", place, url_list) - self.add_media_ref_list(place, media_list) - self.add_citation_list(place, citation_list) - self.add_note_list(place, note_list) - self.add_tag_list(place, tag_list) - self.add_place_ref_list(place, place_ref_list) - self.add_alt_name_list(place, alt_name_list) - count = 1 - for loc_data in alt_location_list: - self.add_location("place", place, loc_data, count) - count + 1 - place.save_cache() - - def add_tag(self, data): - (handle, - name, - color, - priority, - change) = data - tag = models.Tag(handle=handle, - gramps_id=create_id(), - name=name, - color=color, - priority=priority, - last_changed=todate(change)) - tag.save(save_cache=False) - - def add_tag_detail(self, data): - (handle, - name, - color, - priority, - change) = data - tag = models.Tag.objects.get(handle=handle) - tag.save() - - def add_media(self, data): - (handle, gid, path, mime, desc, - checksum, - attribute_list, - citation_list, - note_list, - change, - date, - tag_list, - private) = data - media = models.Media(handle=handle, gramps_id=gid, - path=path, mime=mime, checksum=checksum, - desc=desc, last_changed=todate(change), - private=private) - self.add_date(media, date) - media.save(save_cache=False) - - def add_media_detail(self, data): - (handle, gid, path, mime, desc, - checksum, - attribute_list, - citation_list, - note_list, - change, - date, - tag_list, - private) = data - try: - media = models.Media.objects.get(handle=handle) - except: - print(("ERROR: Media does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - media.save(save_cache=False) - self.add_note_list(media, note_list) - self.add_citation_list(media, citation_list) - self.add_attribute_list(media, attribute_list) - self.add_tag_list(media, tag_list) - media.save_cache() - - def add_event(self, data): - (handle, gid, the_type, date, description, place_handle, - citation_list, note_list, media_list, attribute_list, - change, tag_list, private) = data - event = models.Event(handle=handle, - gramps_id=gid, - event_type=models.get_type(models.EventType, the_type), - private=private, - description=description, - last_changed=todate(change)) - self.add_date(event, date) - event.save(save_cache=False) - - def add_event_detail(self, data): - (handle, gid, the_type, date, description, place_handle, - citation_list, note_list, media_list, attribute_list, - change, tag_list, private) = data - try: - event = models.Event.objects.get(handle=handle) - except: - print(("ERROR: Event does not exist: '%s'" % - str(handle)), file=sys.stderr) - return - try: - place = models.Place.objects.get(handle=place_handle) - except: - place = None - print(("ERROR: Place does not exist: '%s'" % - str(place_handle)), file=sys.stderr) - event.place = place - event.save(save_cache=False) - self.add_note_list(event, note_list) - self.add_attribute_list(event, attribute_list) - self.add_media_ref_list(event, media_list) - self.add_citation_list(event, citation_list) - self.add_tag_list(event, tag_list) - event.save_cache() - - def get_raw(self, item): - """ - Build and return the raw, serialized data of an object. - """ - if isinstance(item, models.Person): - raw = self.get_person(item) - elif isinstance(item, models.Family): - raw = self.get_family(item) - elif isinstance(item, models.Place): - raw = self.get_place(item) - elif isinstance(item, models.Media): - raw = self.get_media(item) - elif isinstance(item, models.Source): - raw = self.get_source(item) - elif isinstance(item, models.Citation): - raw = self.get_citation(item) - elif isinstance(item, models.Repository): - raw = self.get_repository(item) - elif isinstance(item, models.Note): - raw = self.get_note(item) - elif isinstance(item, models.Event): - raw = self.get_event(item) - else: - raise Exception("Don't know how to get raw '%s'" % type(item)) - return raw - - def check_caches(self, callback=None): - """ - Call this to check the caches for all primary models. - """ - if not isinstance(callback, collections.Callable): - callback = lambda percent: None # dummy - - callback(0) - count = 0.0 - total = (self.Note.all().count() + - self.Person.all().count() + - self.Event.all().count() + - self.Family.all().count() + - self.Repository.all().count() + - self.Place.all().count() + - self.Media.all().count() + - self.Source.all().count() + - self.Citation.all().count() + - self.Tag.all().count()) - - for item in self.Note.all(): - raw = self.get_note(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Person.all(): - raw = self.get_person(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Family.all(): - raw = self.get_family(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Source.all(): - raw = self.get_source(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Event.all(): - raw = self.get_event(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Repository.all(): - raw = self.get_repository(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Place.all(): - raw = self.get_place(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Media.all(): - raw = self.get_media(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Citation.all(): - raw = self.get_citation(item) - check_diff(item, raw) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Tag.all(): - raw = self.get_tag(item) - check_diff(item, raw) - count += 1 - callback(100) - - def check_families(self): - """ - Check family structures. - """ - for family in self.Family.all(): - if family.mother: - if not family in family.mother.families.all(): - print("Mother not in family", mother, family) - if family.father: - if not family in family.father.families.all(): - print("Father not in family", mother, family) - for child in family.get_children(): - if family not in child.parent_families.all(): - print("Child not in family", child, family) - for person in self.Person.all(): - for family in person.families.all(): - if person not in [family.mother, family.father]: - print("Spouse not in family", person, family) - for family in person.parent_families.all(): - if person not in family.get_children(): - print("Child not in family", person, family) - - def is_public(self, obj, objref): - """ - Returns whether or not an item is "public", and the reason - why/why not. - - @param obj - an instance of any Primary object - @param objref - one of the PrimaryRef.objects - @return - a tuple containing a boolean (public?) and reason. - - There are three reasons why an item might not be public: - 1) The item itself is private. - 2) The item is referenced by a living Person. - 3) The item is referenced by some other private item. - """ - # If it is private, then no: - if obj.private: - return (False, "It is marked private.") - elif hasattr(obj, "probably_alive") and obj.probably_alive: - return (False, "It is marked probaby alive.") - elif hasattr(obj, "mother") and obj.mother: - public, reason = self.is_public(obj.mother, self.PersonRef) - if not public: - return public, reason - elif hasattr(obj, "father") and obj.father: - public, reason = self.is_public(obj.father, self.PersonRef) - if not public: - return public, reason - # FIXME: what about Associations... anything else? Check PrivateProxy - if objref: - if hasattr(objref.model, "ref_object"): - obj_ref_list = objref.filter(ref_object=obj) - elif hasattr(objref.model, "citation"): - obj_ref_list = objref.filter(citation=obj) - else: - raise Exception("objref '%s' needs a ref for '%s'" % (objref.model, obj)) - for reference in obj_ref_list: - ref_from_class = reference.object_type.model_class() - item = None - try: - item = ref_from_class.objects.get(id=reference.object_id) - except: - print("Warning: Corrupt reference: %s" % str(reference)) - continue - # If it is linked to by someone alive? public = False - if hasattr(item, "probably_alive") and item.probably_alive: - return (False, "It is referenced by someone who is probaby alive.") - # If it is linked to by something private? public = False - elif item.private: - return (False, "It is referenced by an item which is marked private.") - return (True, "It is visible to the public.") - - def update_public(self, obj, save=True): - """ - >>> dji.update_public(event) - - Given an Event or other instance, update the event's public - status, or any event referenced to by the instance. - - For example, if a person is found to be alive, then the - referenced events should be marked not public (public = False). - - """ - from gramps.webapp.utils import probably_alive - if obj.__class__.__name__ == "Event": - objref = self.EventRef - elif obj.__class__.__name__ == "Person": - objref = self.PersonRef - elif obj.__class__.__name__ == "Note": - objref = self.NoteRef - elif obj.__class__.__name__ == "Repository": - objref = self.RepositoryRef - elif obj.__class__.__name__ == "Citation": - objref = self.CitationRef - elif obj.__class__.__name__ == "Media": - objref = self.MediaRef - elif obj.__class__.__name__ == "Place": # no need for dependency - objref = None - elif obj.__class__.__name__ == "Source": # no need for dependency - objref = None - elif obj.__class__.__name__ == "Family": - objref = self.ChildRef # correct? - else: - raise Exception("Can't compute public of type '%s'" % str(obj)) - public, reason = self.is_public(obj, objref) # correct? - # Ok, update, if needed: - if obj.public != public: - obj.public = public - if save: - print("Updating public:", obj.__class__.__name__, obj.gramps_id) - obj.save() - #log = self.Log() - #log.referenced_by = obj - #log.object_id = obj.id - #log.object_type = obj_type - #log.log_type = "update public status" - #log.reason = reason - #log.order = 0 - #log.save() - - def update_publics(self, callback=None): - """ - Call this to update probably_alive for all primary models. - """ - if not isinstance(callback, collections.Callable): - callback = lambda percent: None # dummy - - callback(0) - count = 0.0 - total = (self.Note.all().count() + - self.Person.all().count() + - self.Event.all().count() + - self.Family.all().count() + - self.Repository.all().count() + - self.Place.all().count() + - self.Media.all().count() + - self.Source.all().count() + - self.Citation.all().count()) - - for item in self.Note.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Person.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Family.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Source.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Event.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Repository.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Place.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Media.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - for item in self.Citation.all(): - self.update_public(item) - count += 1 - callback(100 * (count/total if total else 0)) - - def update_probably_alive(self, callback=None): - """ - Call this to update primary_alive for people. - """ - from gramps.webapp.utils import probably_alive - if not isinstance(callback, collections.Callable): - callback = lambda percent: None # dummy - callback(0) - count = 0.0 - total = self.Person.all().count() - for item in self.Person.all(): - pa = probably_alive(item.handle) - if pa != item.probably_alive: - print("Updating probably_alive") - item.probably_alive = pa - item.save() - count += 1 - callback(100 * (count/total if total else 0)) diff --git a/gramps/webapp/manage.py b/gramps/webapp/manage.py deleted file mode 100755 index 5e3cd5163..000000000 --- a/gramps/webapp/manage.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Manage Django """ - -#------------------------------------------------------------------------ -# -# Django Modules -# -#------------------------------------------------------------------------ - -# from django.core.management import execute_manager -# try: -# import settings # Assumed to be in the same directory. -# except ImportError: -# import sys -# sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) -# sys.exit(1) - -# if __name__ == "__main__": -# execute_manager(settings) - -import os, sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gramps.webapp.default_settings") - - from django.core.management import execute_from_command_line - - execute_from_command_line(sys.argv) diff --git a/gramps/webapp/modules_checkpoint.py b/gramps/webapp/modules_checkpoint.py deleted file mode 100644 index a690a2502..000000000 --- a/gramps/webapp/modules_checkpoint.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -class ModulesCheckpoint(object): - def __init__(self): - self.original = sys.modules.copy() - - def reset(self): - # clear modules: - for key in list(sys.modules.keys()): - del(sys.modules[key]) - # load previous: - for key in self.original: - sys.modules[key] = self.original[key] diff --git a/gramps/webapp/reports.py b/gramps/webapp/reports.py deleted file mode 100644 index 347c79de8..000000000 --- a/gramps/webapp/reports.py +++ /dev/null @@ -1,143 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -# webapp/reports.py - -# imports for import/export: - -from gramps.gen.dbstate import DbState -from gramps.cli.grampscli import CLIManager -from gramps.gen.plug import BasePluginManager -from gramps.cli.user import User as GUser # gramps user - -import os - -# Example for running a report: -# ------------------------------ -# from gramps.cli.plug import run_report -# from django.conf import settings -# import webapp.settings as default_settings -# try: -# settings.configure(default_settings) -# except: -# pass -# import djangodb -# db = djangodb.DbDjango() -# run_report(db, "ancestor_report", off="txt", of="ar.txt", pid="I0363") - -def get_plugin_options(db, pid): - """ - Get the default options and help for this plugin. - """ - dbstate = DbState() - climanager = CLIManager(dbstate, setloader=False, user=GUser()) # do not load db_loader - climanager.do_reg_plugins(dbstate, None) - pmgr = BasePluginManager.get_instance() - pdata = pmgr.get_plugin(pid) - if hasattr(pdata, "optionclass") and pdata.optionclass: - mod = pmgr.load_plugin(pdata) - optionclass = eval("mod." + pdata.optionclass) - optioninstance = optionclass("Name", db) - optioninstance.load_previous_values() - return optioninstance.options_dict, optioninstance.options_help - else: - return {}, {} - -def import_file(db, filename, user): - """ - Import a file (such as a GEDCOM file) into the given db. - - >>> import_file(DbDjango(), "/home/user/Untitled_1.ged", User()) - """ - from .grampsdb.models import Person - dbstate = DbState() - climanager = CLIManager(dbstate, setloader=False, user=user) # do not load db_loader - climanager.do_reg_plugins(dbstate, None) - pmgr = BasePluginManager.get_instance() - (name, ext) = os.path.splitext(os.path.basename(filename)) - format = ext[1:].lower() - import_list = pmgr.get_reg_importers() - for pdata in import_list: - if format == pdata.extension: - mod = pmgr.load_plugin(pdata) - if not mod: - for item in pmgr.get_fail_list(): - name, error_tuple, pdata = item - # (filename, (exception-type, exception, traceback), pdata) - etype, exception, traceback = error_tuple - print("ERROR:", name, exception) - return False - import_function = getattr(mod, pdata.import_function) - retval = import_function(db, filename, user) - return retval - return False - -def download(url, filename=None): - from urllib.request import Request, urlopen - from urllib.parse import urlsplit - import shutil - def getFilename(url,openUrl): - if 'Content-Disposition' in openUrl.info(): - # If the response has Content-Disposition, try to get filename from it - cd = dict([x.strip().split('=') if '=' in x else (x.strip(),'') - for x in openUrl.info().split(';')]) - if 'filename' in cd: - fname = cd['filename'].strip("\"'") - if fname: return fname - # if no filename was found above, parse it out of the final URL. - return os.path.basename(urlsplit(openUrl.url)[2]) - r = urlopen(Request(url)) - success = None - try: - filename = filename or "/tmp/%s" % getFilename(url,r) - with open(filename, 'wb') as f: - shutil.copyfileobj(r,f) - success = filename - finally: - r.close() - return success - -def export_file(db, filename, user): - """ - Export the db to a file (such as a GEDCOM file). - - >>> export_file(DbDjango(), "/home/user/Untitled_1.ged", User()) - """ - dbstate = DbState() - climanager = CLIManager(dbstate, setloader=False, user=user) # do not load db_loader - climanager.do_reg_plugins(dbstate, None) - pmgr = BasePluginManager.get_instance() - (name, ext) = os.path.splitext(os.path.basename(filename)) - format = ext[1:].lower() - export_list = pmgr.get_reg_exporters() - for pdata in export_list: - if format == pdata.extension: - mod = pmgr.load_plugin(pdata) - if not mod: - for item in pmgr.get_fail_list(): - name, error_tuple, pdata = item - etype, exception, traceback = error_tuple - print("ERROR:", name, exception) - return False - export_function = getattr(mod, pdata.export_function) - export_function(db, filename, user) - return True - return False - diff --git a/gramps/webapp/shell.py b/gramps/webapp/shell.py deleted file mode 100644 index a46df8778..000000000 --- a/gramps/webapp/shell.py +++ /dev/null @@ -1,106 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2012 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -#### This sets up Django so you can interact with it via the Python -#### command line. -#### Start with something like: -#### $ PYTHONPATH=..:../plugins/lib python -i shell.py -#### >>> Person.objects.all() - -import os -os.environ['GRAMPS_RESOURCES'] = os.path.dirname(os.path.abspath("..")) -pystartup = os.path.expanduser("~/.pystartup") -if not os.path.exists(pystartup): - fp = file(pystartup, "w") - fp.write(""" -import atexit -import os -import readline -import rlcompleter -import sys - -# change autocomplete to tab -readline.parse_and_bind("tab: complete") - -historyPath = os.path.expanduser("~/.pyhistory") - -def save_history(historyPath=historyPath): - import readline - readline.write_history_file(historyPath) - -if os.path.exists(historyPath): - readline.read_history_file(historyPath) - -atexit.register(save_history) - -# anything not deleted (sys and os) will remain in the interpreter session -del atexit, readline, rlcompleter, save_history, historyPath""") - fp.close() - -with open(pystartup) as f: - code = compile(f.read(), pystartup, 'exec') - exec(code, globals(), locals()) - -from django.conf import settings -from gramps.webapp import default_settings -try: - settings.configure(default_settings) -except RuntimeError: - # already configured; ignore - pass - -# For Django 1.6: -import django -django.setup() - -from gramps.webapp.grampsdb.models import * -from gramps.webapp.grampsdb.forms import * -from gramps.webapp.djangodb import DbDjango -from gramps.webapp.reports import import_file, export_file -from gramps.webapp.libdjango import DjangoInterface, totime, todate -from gramps.gen.datehandler import displayer, parser -from gramps.webapp.utils import StyledNoteFormatter, parse_styled_text -from gramps.gen.lib import StyledText -from gramps.cli.user import User as GUser # gramps user - -from django.db.models import Q - -db = DbDjango() - -db.load(os.path.abspath(os.path.dirname(__file__))) -dd = displayer.display -dp = parser.parse - -#import_file(db, -# "/home/dblank/gramps/trunk/example/gramps/data.gramps", -# GUser()) - -#snf = StyledNoteFormatter(db) -#for n in Note.objects.all(): -# note = db.get_note_from_handle(n.handle) -# print snf.format(note) - -#note = Note.objects.get(handle="aef30789d3d2090abe2") -#genlibnote = db.get_note_from_handle(note.handle) -#html_text = snf.format(genlibnote) -## FIXME: this looks wrong: -#print html_text -#print parse_styled_text(html_text) - -##st = StyledText(note.text, dji.get_note_markup(note)) diff --git a/gramps/webapp/shell.sh b/gramps/webapp/shell.sh deleted file mode 100755 index 2c9b444b1..000000000 --- a/gramps/webapp/shell.sh +++ /dev/null @@ -1 +0,0 @@ -PYTHONPATH=../.. python3 -i shell.py diff --git a/gramps/webapp/urls.py b/gramps/webapp/urls.py deleted file mode 100644 index 2be960618..000000000 --- a/gramps/webapp/urls.py +++ /dev/null @@ -1,130 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Url handler """ - -#------------------------------------------------------------------------ -# -# Python Modules -# -#------------------------------------------------------------------------ -import os - -#------------------------------------------------------------------------ -# -# Django and Gramps Modules -# -#------------------------------------------------------------------------ -from gramps.gen.const import IMAGE_DIR, ROOT_DIR, DATA_DIR -from django.conf.urls import patterns, url, include -from django.contrib import admin - -admin.autodiscover() - -from gramps.webapp.grampsdb.views import * - -urlpatterns = patterns('', - # Specific matches first: - url(r'^admin/', include(admin.site.urls)), -) - -urlpatterns += patterns('', - # Static serves! DANGEROUS in production: - (r'^styles/(?P.*)$', 'django.views.static.serve', - {'document_root': DATA_DIR, -# os.path.join(ROOT_DIR, "plugins", "webstuff"), - 'show_indexes': True}, - ), - (r'^images/(?P.*)$', 'django.views.static.serve', - {'document_root': IMAGE_DIR, - 'show_indexes': True}, - ), - # Django 1.5.4 - (r'^static/(?P.*)$', 'django.views.static.serve', - {'document_root': '/usr/share/pyshared/django/contrib/admin/static/', - 'show_indexes': True}, - ), - # Django 1.5.4 -) - -# The rest will match views: -urlpatterns += patterns('', - (r'^$', main_page), - ## Prior to django 1.5, use this: - ##(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', - ##{'url': '/styles/images/favicon.ico'}), - # Django 1.5.4: - (r'^favicon\.ico$', 'django.shortcuts.redirect', - {'url': '/styles/images/favicon.ico'}), - (r'^user/$', user_page), - (r'^json/$', process_json_request), - (r'^user/(\w+)/$', user_page), - (r'^browse/$', browse_page), - (r'^login/$', 'django.contrib.auth.views.login'), - (r'^logout/$', logout_page), - (r'^(?P(\w+))/$', - view_list), # /view/ - (r'^(?P(\w+))/add$', - action, - {"handle": None, "act": "add"}), # /view/add - (r'^(?P(\w+))/add/(?P(\w+))/(?P(\w+))$', - add_to), # /view/add/item/handle - (r'^(?P(\w+))/share/(?P(\w+))/(?P(\w+))$', - add_share), # /view/share/item/handle - (r'^(?P(\w+))/(?P(\w+))/$', action, - {"act": "view"}), # /view/handle/ - (r'^(?P(\w+))/(?P(\w+))/(?P(\w+))$', - action), # /view/handle/action - (r'^(?P(\w+))/(?P(\w+))/reference/(?P(\w+))/(?P(\w+))$', - process_reference), # /view/handle/reference/item/order - (r'^person/(?P(\w+))/name/(?P(\w+))$', process_name), - (r'^person/(?P(\w+))/name/(?P(\w+))/(?P(\w+))$', - process_name), - - (r'^person/(?P(\w+))/name/(?P(\w+))/surname/(?P(\w+))$', - process_surname), - (r'^person/(?P(\w+))/name/(?P(\w+))/surname/(?P(\w+))/(?P(\w+))$', - process_surname), - (r'^family/(?P(\w+))/(?P(\w+))/child/(?P(\w+))$', process_child), -## (r'^profile/(?P(\w+)/)$', ), - (r'^(?P(\w+))/(?P(\w+))/(?P(\w+))/(?P(\w+))/(?P(\w+))$', - process_list_item), - (r'^note/(?P(\w+))/person/(?P(\w+))/name/(?P(\w+))$', - process_note_on_name), -) - -# In urls: -# urlpatterns = patterns('', -# url(r'^archive/(\d{4})/$', archive, name="full-archive"), -# url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"), -# ) - -# In template: -# {% url arch-summary 1945 %} -# {% url full-archive 2007 %} -#{% url path.to.view as the_url %} -#{% if the_url %} -# Link to optional stuff -#{% endif %} - -# In code: -#from django.core.urlresolvers import reverse -# -#def myview(request): -# return HttpResponseRedirect(reverse('arch-summary', args=[1945])) diff --git a/gramps/webapp/utils.py b/gramps/webapp/utils.py deleted file mode 100644 index 408ecc0b5..000000000 --- a/gramps/webapp/utils.py +++ /dev/null @@ -1,1713 +0,0 @@ -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2009 Douglas S. Blank -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -""" Django/Gramps utilities """ - -#------------------------------------------------------------------------ -# -# Python Modules -# -#------------------------------------------------------------------------ -import sys -import os -import re -import datetime -from html.parser import HTMLParser - -#------------------------------------------------------------------------ -# -# Django Modules -# -#------------------------------------------------------------------------ -from django.utils.safestring import mark_safe -from django.contrib.contenttypes.models import ContentType -from django.db.models import Q - -#------------------------------------------------------------------------ -# -# Gramps-Connect Modules -# -#------------------------------------------------------------------------ -import gramps.webapp.grampsdb.models as models -import gramps.webapp.grampsdb.forms as forms -from gramps.webapp import libdjango -from gramps.webapp.djangodb import DbDjango -from gramps.cli.user import User as GUser # gramps user - -#------------------------------------------------------------------------ -# -# Gramps Modules -# -#------------------------------------------------------------------------ -from gramps.gen.simple import SimpleTable, SimpleAccess, make_basic_stylesheet -from gramps.gen.utils.alive import probably_alive as alive -from gramps.gen.dbstate import DbState -from gramps.gen.datehandler import displayer, parser -from gramps.gen.lib.date import Date as GDate, Today -from gramps.gen.lib import Person -from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback -from gramps.gen.plug import BasePluginManager -from gramps.cli.grampscli import CLIManager -from gramps.gen.utils.grampslocale import GrampsLocale - -#FIXME: A locale should be obtained from the user and used to -#initialize the locale. Passing in lang and language parameters to the -#constructor prevents querying the environment. -glocale = GrampsLocale(lang='en_US.UTF-8', languages='en') -_ = glocale.translation.gettext - -TAB_HEIGHT = 200 - -util_filters = [ - 'nbsp', - 'date_as_text', - 'render_name', - ] - -util_tags = [ - 'render', - 'media_link', - 'render_name', - "get_person_from_handle", - "event_table", - "history_table", - "name_table", - "surname_table", - "citation_table", - "note_table", - "attribute_table", - "data_table", - "address_table", - "media_table", - "internet_table", - "association_table", - "location_table", - "lds_table", - "repository_table", - "person_reference_table", - "note_reference_table", - "event_reference_table", - "repository_reference_table", - "citation_reference_table", - "source_reference_table", - "media_reference_table", - "tag_reference_table", - "place_reference_table", - "children_table", - "make_button", - ] - -#------------------------------------------------------------------------ -# -# Module Constants -# -#------------------------------------------------------------------------ -dd = displayer.display -dp = parser.parse -db = DbDjango() -db.load(os.path.abspath(os.path.dirname(__file__))) - -def register_plugins(user): - dbstate = DbState() - climanager = CLIManager(dbstate, setloader=False, user=user) # don't load db - climanager.do_reg_plugins(dbstate, None) - pmgr = BasePluginManager.get_instance() - return pmgr - -def get_person_from_handle(db, handle): - # db is a Gramps Db interface - # handle is a Person Handle - try: - return db.get_person_from_handle(handle) - except: - print("error in get_person_from_handle:", file=sys.stderr) - import sys, traceback - cla, exc, trbk = sys.exc_info() - print(_("Error") + (" : %s %s" %(cla, exc)), file=sys.stderr) - traceback.print_exc() - return None - -def probably_alive(handle): - ## FIXME: need to call after save? - person = db.get_person_from_handle(handle) - if person: - return alive(person, db) - else: - return True - -def format_number(number, with_grouping=True): - if number != "": - return glocale.format("%d", number, with_grouping) - else: - return glocale.format("%d", 0, with_grouping) - -def table_count(table, with_grouping=True): - if table == "person": - number = models.Person.objects.count() - elif table == "family": - number = models.Family.objects.count() - elif table == "event": - number = models.Event.objects.count() - elif table == "note": - number = models.Note.objects.count() - elif table == "media": - number = models.Media.objects.count() - elif table == "citation": - number = models.Citation.objects.count() - elif table == "source": - number = models.Source.objects.count() - elif table == "place": - number = models.Place.objects.count() - elif table == "repository": - number = models.Repository.objects.count() - elif table == "tag": - number = models.Tag.objects.count() - else: - return "[unknown table]" - return glocale.format("%d", number, with_grouping) - -def nbsp(string): - """ - """ - if string: - return string - else: - return mark_safe(" ") - -class Table(object): - """ - >>> table = Table("css_id") - >>> table.columns("Col1", "Col2", "Col3") - >>> table.row("1", "2", "3") - >>> table.row("4", "5", "6") - >>> table.get_html() - """ - def __init__(self, id, style=None): - self.id = id # css id - self.db = db - self.access = SimpleAccess(self.db) - self.table = SimpleTable(self.access) - self.column_widths = None - class Doc(object): - def __init__(self, doc): - self.doc = doc - self.doc.set_link_attrs({"class": "browsecell"}) - # None is paperstyle, which is ignored: - self.doc = Doc(HtmlDoc( - make_basic_stylesheet( - Table={"set_width":95}, - TableHeaderCell={"set_bottom_border": True, - "set_right_border": True, - "set_padding": .1, - }, - TableDataCell={"set_bottom_border": True, - "set_right_border": True, - "set_padding": .1, - }, - ), - None)) - self.doc.doc._backend = HtmlBackend() - self.doc.doc.use_table_headers = True - # You can set elements id, class, etc: - self.doc.doc.htmllist += [ - Html('div', - class_="content", - id=self.id, - style=("overflow: auto; height:%spx; background-color: #f4f0ec;" % TAB_HEIGHT) if not style else style)] - - def columns(self, *args): - self.table.columns(*args) - - def row(self, *args): - self.table.row(*list(map(nbsp, args))) - - def link(self, object_type_name, handle): - self.table.set_link_col((object_type_name, handle)) - - def links(self, links): - """ - A list of (object_type_name, handle) pairs, one per row. - """ - self.table.set_link_col(links) - - def get_html(self): - retval = "" - # The HTML writer escapes data: - self.table.write(self.doc, self.column_widths) # forces to htmllist - # FIXME: do once, or once per table? - self.doc.doc.build_style_declaration(self.id) # can pass id, for whole - # FIXME: don't want to repeat this, unless diff for each table: - retval += "" % self.doc.doc.style_declaration - # We have a couple of HTML bits that we want to unescape: - return retval + str(self.doc.doc.htmllist[0]).replace("&nbsp;", " ") - -def build_args(**kwargs): - retval = "" - first = True - for key in kwargs: - if kwargs[key] is not "": - if first: - retval+= "?" - first = False - else: - retval += "&" - retval += "%s=%s" % (key, kwargs[key]) - return retval - -def build_search(request): - search = request.GET.get("search", "") or request.POST.get("search", "") - page = request.GET.get("page", "") or request.POST.get("page", "") - return build_args(search=search, page=page) - -def make_button(text, url, *args): - newargs = [] - kwargs = "" - last = "" - for arg in args: - if isinstance(arg, str) and arg.startswith("?"): - kwargs = arg - elif isinstance(arg, str) and arg.startswith("#"): - last = arg - elif arg == "": - pass - else: - newargs.append(arg) - if newargs: - url = url % tuple(newargs) - if text[0] in "+$-?x" or text in ["x", "^", "v", "<", "<<", ">", ">>"]: - return mark_safe(make_image_button(text, url, kwargs, last)) - else: - return mark_safe("""""" % - (text, url, kwargs, last)) - -def make_image_button(text, url, kwargs, last): - if text == "x": - button = "x" - text = "Delete row" - elif text == "^": - button = "^" - text = "Move row up" - elif text == "v": - button = "v" - text = "Move row down" - elif text.startswith("+"): - button = "+" - text = text[1:] - elif text.startswith("<"): - button = "<" - text = text[1:] - elif text.startswith("<<"): - button = "<<" - text = text[2:] - elif text.startswith(">"): - button = ">" - text = text[1:] - elif text.startswith(">>"): - button = ">>" - text = text[2:] - elif text.startswith("-"): - button = "x" - text = text[1:] - elif text.startswith("$"): - button = "$" - text = text[1:] - elif text.startswith("?"): - button = "?" - text = text[1:] - elif text.startswith("x"): - button = "cancel" - text = text[1:] - return make_image_button2(button, text, url, kwargs, last) - -def make_image_button2(button, text, url, kwargs="", last=""): - if button == "cancel": - filename = "/images/gtk-remove.png" - elif button == "x": # delete - filename = "/images/gtk-remove.png" - elif button == "^": # move up - filename = "/images/up.png" - elif button == "v": # move down - filename = "/images/down.png" - elif button == "<": # prev - filename = "/images/previous.png" - elif button == "<<": # start - filename = "/images/player-start.png" - elif button == ">": # next - filename = "/images/next.png" - elif button == ">>": # end - filename = "/images/player-end.png" - elif button == "+": # add - filename = "/images/add.png" - elif button == "$": # pick, share - filename = "/images/stock_index_24.png" - elif button == "?": # edit - filename = "/images/text-editor.png" - elif button == "add child to existing family": - filename = "/images/gramps-parents-open.png" - elif button == "add child to new family": - filename = "/images/gramps-parents-add.png" - elif button == "add spouse to existing family": - filename = "/images/add-parent-existing-family.png" - elif button == "add spouse to new family": - filename = "/images/gramps-parents.png" - return """%s""" % (text, text, filename, url, kwargs, last) - -def event_table(obj, user, act, url, args): - retval = "" - has_data = False - cssid = "tab-events" - table = Table("event_table") - table.columns( - "", - _("Description"), - _("Type"), - _("ID"), - _("Date"), - _("Place"), - _("Role")) - table.column_widths = [11, 19, 10, 7, 20, 23, 10] - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - event_ref_list = models.EventRef.objects.filter( - object_id=obj.id, - object_type=obj_type).order_by("order") - event_list = [(o.ref_object, o) for o in event_ref_list] - links = [] - count = 1 - for (djevent, event_ref) in event_list: - table.row(Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser and act == "view" else "", - djevent.description, - table.db.get_event_from_handle(djevent.handle), - djevent.gramps_id, - display_date(djevent), - get_title(djevent.place), - str(event_ref.role_type)) - links.append(('URL', event_ref.get_url())) - has_data = True - count += 1 - table.links(links) - retval += """
""" - if user.is_superuser and act == "view": - retval += make_button(_("+Add New Event"), (url % args).replace("$act", "add")) - retval += make_button(_("$Add Existing Event"), (url % args).replace("$act", "share")) - else: - retval += """
""" # to keep tabs same height - retval += """
""" - retval += table.get_html() - if user.is_superuser and act == "view": - count = 1 - retval = retval.replace("{{", """
""") - retval = retval.replace("}}", """
""") - for (djevent, event_ref) in event_list: - item = obj.__class__.__name__.lower() - retval = retval.replace("[[x%d]]" % count, make_button("x", "/%s/%s/remove/eventref/%d" % (item, obj.handle, count))) - retval = retval.replace("[[^%d]]" % count, make_button("^", "/%s/%s/up/eventref/%d" % (item, obj.handle, count))) - retval = retval.replace("[[v%d]]" % count, make_button("v", "/%s/%s/down/eventref/%d" % (item, obj.handle, count))) - count += 1 - if has_data: - retval += """ \n""" % cssid - return retval - -def history_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-history" - table = Table("history_table") - table.columns( - _("Action"), - _("Comment"), - ) - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - for entry in models.Log.objects.filter( - object_id=obj.id, - object_type=obj_type): - table.row( - "%s on %s by %s" % (entry.log_type, - entry.last_changed, - entry.last_changed_by), - entry.reason) - has_data = True - table.row( - "Latest on %s by %s" % (obj.last_changed, - obj.last_changed_by), - "Current status") - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def name_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-names" - table = Table("name_table") - table.columns(_("Name"), - _("Type"), - _("Group As"), - _("Source"), - _("Note Preview")) - if user.is_authenticated() or obj.public: - links = [] - for name in obj.name_set.all().order_by("order"): - obj_type = ContentType.objects.get_for_model(name) - citationq = db.dji.CitationRef.filter(object_type=obj_type, - object_id=name.id).count() > 0 - note_refs = db.dji.NoteRef.filter(object_type=obj_type, - object_id=name.id) - note = "" - if note_refs.count() > 0: - try: - note = db.dji.Note.get(id=note_refs[0].object_id).text[:50] - except: - note = None - table.row(make_name(name, user), - str(name.name_type) + ["", " (preferred)"][int(name.preferred)], - name.group_as, - ["No", "Yes"][citationq], - note) - links.append(('URL', - # url is "/person/%s/name" - (url % name.person.handle) + ("/%s" % name.order))) - has_data = True - table.links(links) - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Name"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def surname_table(obj, user, act, url=None, *args): - person_handle = args[0] - order = args[1] - retval = "" - has_data = False - cssid = "tab-surnames" - table = Table("surname_table") - table.columns(_("Order"), _("Surname"),) - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Surname"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - if user.is_authenticated() or obj.public: - try: - name = obj.name_set.filter(order=order)[0] - except: - name = None - if name: - for surname in name.surname_set.all().order_by("order"): - table.row(str(surname.order), surname) - has_data = True - retval += table.get_html() - else: - retval += "

No such name order = %s

" % order - if has_data: - retval += """ \n""" % cssid - return retval - -def citation_table(obj, user, act, url=None, *args): - # FIXME: how can citation_table and source_table both be on same - # page? This causes problems with form names, tab names, etc. - retval = "" - has_data = False - cssid = "tab-sources" - table = Table("citation_table") - table.columns("", - _("ID"), - _("Confidence"), - _("Page")) - table.column_widths = [11, 10, 49, 30] - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - citation_refs = db.dji.CitationRef.filter(object_type=obj_type, - object_id=obj.id).order_by("order") - links = [] - count = 1 - for citation_ref in citation_refs: - if citation_ref.citation: - citation = table.db.get_citation_from_handle( - citation_ref.citation.handle) - table.row(Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser and url and act == "view" else "", - citation.gramps_id, - str(citation.confidence), - str(citation.page), - ) - links.append(('URL', citation_ref.get_url())) - has_data = True - count += 1 - table.links(links) - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add New Citation"), (url % args).replace("$act", "add")) - retval += make_button(_("$Add Existing Citation"), (url % args).replace("$act", "share")) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if user.is_superuser and url and act == "view": - retval = retval.replace("{{", """
""") - retval = retval.replace("}}", """
""") - count = 1 - for citation_ref in citation_refs: - item = obj.__class__.__name__.lower() - retval = retval.replace("[[x%d]]" % count, make_button("x", "/%s/%s/remove/citationref/%d" % (item, obj.handle, count))) - retval = retval.replace("[[^%d]]" % count, make_button("^", "/%s/%s/up/citationref/%d" % (item, obj.handle, count))) - retval = retval.replace("[[v%d]]" % count, make_button("v", "/%s/%s/down/citationref/%d" % (item, obj.handle, count))) - count += 1 - if has_data: - retval += """ \n""" % cssid - return retval - -def repository_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-repositories" - table = Table("repository_table") - table.columns( - "", - _("ID"), - _("Title"), - _("Call number"), - _("Type"), - ) - table.column_widths = [11, 49, 20, 20] - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add New Repository"), (url % args).replace("$act", "add")) - retval += make_button(_("$Add Existing Repository"), (url % args).replace("$act", "share")) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - refs = db.dji.RepositoryRef.filter(object_type=obj_type, - object_id=obj.id) - count = 1 - for repo_ref in refs: - repository = repo_ref.ref_object - table.row( - Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser else "", - repository.gramps_id, - repository.name, - repo_ref.call_number, - str(repository.repository_type), - ) - has_data = True - count += 1 - text = table.get_html() - text = text.replace("{{", """
""") - text = text.replace("}}", """
""") - count = 1 - for repo_ref in refs: - item = obj.__class__.__name__.lower() - text = text.replace("[[x%d]]" % count, make_button("x", "/%s/%s/remove/repositoryref/%d" % (item, obj.handle, count))) - text = text.replace("[[^%d]]" % count, make_button("^", "/%s/%s/up/repositoryref/%d" % (item, obj.handle, count))) - text = text.replace("[[v%d]]" % count, make_button("v", "/%s/%s/down/repositoryref/%d" % (item, obj.handle, count))) - count += 1 - retval += text - if has_data: - retval += """ \n""" % cssid - return retval - -def note_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-notes" - table = Table("note_table") - table.columns( - "", - _("ID"), - _("Type"), - _("Note")) - table.column_widths = [11, 10, 20, 59] - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - note_refs = db.dji.NoteRef.filter(object_type=obj_type, - object_id=obj.id).order_by("order") - links = [] - count = 1 - for note_ref in note_refs: - note = note_ref.ref_object - table.row(Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser else "", - note.gramps_id, - str(note.note_type), - note.text[:50] - ) - links.append(('URL', note_ref.get_url())) - has_data = True - count += 1 - table.links(links) - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add New Note"), (url % args).replace("$act", "add")) - retval += make_button(_("$Add Existing Note"), (url % args).replace("$act", "share")) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - text = table.get_html() - text = text.replace("{{", """
""") - text = text.replace("}}", """
""") - if user.is_authenticated() or obj.public: - count = 1 - for note_ref in note_refs: - item = obj.__class__.__name__.lower() - text = text.replace("[[x%d]]" % count, make_button("x", "/%s/%s/remove/noteref/%d" % (item, obj.handle, count))) - text = text.replace("[[^%d]]" % count, make_button("^", "/%s/%s/up/noteref/%d" % (item, obj.handle, count))) - text = text.replace("[[v%d]]" % count, make_button("v", "/%s/%s/down/noteref/%d" % (item, obj.handle, count))) - count += 1 - retval += text - if has_data: - retval += """ \n""" % cssid - return retval - -def data_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-data" - table = Table("data_table") - table.columns( - "", - _("Type"), - _("Value"), - ) - table.column_widths = [11, 39, 50] - retval += """
""" - if user.is_superuser and url and act == "view": - # /data/$act/citation/%s - retval += make_button(_("+Add Data"), (url.replace("$act", "add") % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - if user.is_authenticated() or obj.public: - item_class = obj.__class__.__name__.lower() - if item_class == "citation": - refs = models.CitationAttribute.objects.filter(citation=obj).order_by("order") - elif item_class == "source": - refs = models.SourceAttribute.objects.filter(source=obj).order_by("order") - count = 1 - for ref in refs: - if item_class == "citation": - ref_obj = ref.citation - elif item_class == "source": - ref_obj = ref.source - table.row( - Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser else "", - ref_obj.key, - ref_obj.value, - ) - has_data = True - count += 1 - text = table.get_html() - text = text.replace("{{", """
""") - text = text.replace("}}", """
""") - count = 1 - for repo_ref in refs: - text = text.replace("[[x%d]]" % count, make_button("x", "/%s/%s/remove/attribute/%d" % (item_class, obj.handle, count))) - text = text.replace("[[^%d]]" % count, make_button("^", "/%s/%s/up/attribute/%d" % (item_class, obj.handle, count))) - text = text.replace("[[v%d]]" % count, make_button("v", "/%s/%s/down/attribute/%d" % (item_class, obj.handle, count))) - count += 1 - retval += text - if has_data: - retval += """ \n""" % cssid - return retval - -def attribute_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-attributes" - table = Table("attribute_table") - table.columns(_("Type"), - _("Value"), - ) - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - attributes = db.dji.Attribute.filter(object_type=obj_type, - object_id=obj.id) - for attribute in attributes: - table.row(attribute.attribute_type.name, - attribute.value) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Attribute"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def address_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-addresses" - table = Table("address_table") - table.columns(_("Date"), - _("Address"), - _("City"), - _("State"), - _("Country")) - if user.is_authenticated() or obj.public: - for address in obj.address_set.all().order_by("order"): - locations = address.location_set.all().order_by("order") - for location in locations: - table.row(display_date(address), - location.street, - location.city, - location.state, - location.country) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Address"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def media_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-media" - table = Table("media_table") - table.columns(_("Description"), - _("Type"), - _("Path/Filename"), - ) - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - media_refs = db.dji.MediaRef.filter(object_type=obj_type, - object_id=obj.id) - for media_ref in media_refs: - media = table.db.get_object_from_handle( - media_ref.ref_object.handle) - table.row(table.db.get_object_from_handle(media.handle), - str(media_ref.ref_object.desc), - media_ref.ref_object.path) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add New Media"), (url % args).replace("$act", "add")) - retval += make_button(_("$Add Existing Media"), (url % args).replace("$act", "share")) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def internet_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-internet" - table = Table("internet_table") - table.columns(_("Type"), - _("Path"), - _("Description")) - if user.is_authenticated() or obj.public: - urls = db.dji.Url.filter(person=obj) - for url_obj in urls: - table.row(str(url_obj.url_type), - url_obj.path, - url_obj.desc) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Internet"), (str(url) % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def association_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-associations" - table = Table("association_table") - table.columns(_("Name"), - _("ID"), - _("Association")) - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Association"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - if user.is_authenticated() or obj.public: - person = table.db.get_person_from_handle(obj.handle) - if person: - links = [] - count = 1 - associations = person.get_person_ref_list() - for association in associations: # PersonRef - table.row(Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser and url and act == "view" else "", - association.ref_object.get_primary_name(), - association.ref_object.gramps_id, - association.description, - ) - links.append(('URL', "/person/%s/association/%d" % (obj.handle, count))) - has_data = True - count += 1 - table.links(links) - text = table.get_html() - text = text.replace("{{", """
""") - text = text.replace("}}", """
""") - count = 1 - for association in associations: # PersonRef - text = text.replace("[[x%d]]" % count, make_button("x", "/person/%s/remove/association/%d" % (obj.handle, count))) - text = text.replace("[[^%d]]" % count, make_button("^", "/person/%s/up/association/%d" % (obj.handle, count))) - text = text.replace("[[v%d]]" % count, make_button("v", "/person/%s/down/association/%d" % (obj.handle, count))) - retval += text - if has_data: - retval += """ \n""" % cssid - return retval - -def location_table(obj, user, act, url=None, *args): - # obj is Place or Address - retval = "" - has_data = False - cssid = "tab-alternatelocations" - table = Table("location_table") - table.columns(_("Street"), - _("Locality"), - _("City"), - _("State"), - _("Country")) - if user.is_authenticated() or obj.public: - # FIXME: location confusion! - # The single Location on the Location Tab is here too? - # I think if Parish is None, then these are single Locations; - # else they are in the table of alternate locations - for location in obj.location_set.all().order_by("order"): - table.row( - location.street, - location.locality, - location.city, - location.state, - location.country) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add Address"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def lds_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-lds" - table = Table("lds_table") - table.columns(_("Type"), - _("Date"), - _("Status"), - _("Temple"), - _("Place")) - if user.is_authenticated() or obj.public: - obj_type = ContentType.objects.get_for_model(obj) - ldss = obj.lds_set.all().order_by("order") - for lds in ldss: - table.row(str(lds.lds_type), - display_date(lds), - str(lds.status), - lds.temple, - get_title(lds.place)) - has_data = True - retval += """
""" - if user.is_superuser and url and act == "view": - retval += make_button(_("+Add LDS"), (url % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += table.get_html() - if has_data: - retval += """ \n""" % cssid - return retval - -def person_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - text1 = "" - text2 = "" - table1 = Table("person_reference_table", style="background-color: #f4f0ec;") - table1.columns( - "As Spouse", - _("ID"), - _("Reference"), - ) - table1.column_widths = [11, 10, 79] - table2 = Table("person_reference_table", style="background-color: #f4f0ec;") - table2.columns( - "As Child", - _("ID"), - _("Reference"), - ) - table2.column_widths = [11, 10, 79] - if (user.is_authenticated() or obj.public) and act != "add": - count = 1 - for through in models.MyFamilies.objects.filter(person=obj).order_by("order"): - reference = through.family - table1.row( - Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser else "", - reference.gramps_id, - reference, - ) - has_data = True - count += 1 - text1 += table1.get_html() - text1 = text1.replace("{{", """
""") - text1 = text1.replace("}}", """
""") - count = 1 - for through in models.MyFamilies.objects.filter(person=obj).order_by("order"): - reference = through.family - text1 = text1.replace("[[x%d]]" % count, make_button("x", "/person/%s/remove/family/%d" % (obj.handle, count))) - text1 = text1.replace("[[^%d]]" % count, make_button("^", "/person/%s/up/family/%d" % (obj.handle, count))) - text1 = text1.replace("[[v%d]]" % count, make_button("v", "/person/%s/down/family/%d" % (obj.handle, count))) - count += 1 - # Parent Families - count = 1 - for through in models.MyParentFamilies.objects.filter(person=obj).order_by("order"): - reference = through.family - table2.row( - Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser else "", - reference.gramps_id, - reference, - ) - has_data = True - count += 1 - text2 += table2.get_html() - text2 = text2.replace("{{", """
""") - text2 = text2.replace("}}", """
""") - count = 1 - for through in models.MyParentFamilies.objects.filter(person=obj).order_by("order"): - reference = through.family - text2 = text2.replace("[[x%d]]" % count, make_button("x", "/person/%s/remove/parentfamily/%d" % (obj.handle, count))) - text2 = text2.replace("[[^%d]]" % count, make_button("^", "/person/%s/up/parentfamily/%d" % (obj.handle, count))) - text2 = text2.replace("[[v%d]]" % count, make_button("v", "/person/%s/down/parentfamily/%d" % (obj.handle, count))) - count += 1 - - retval += """
""" - retval += make_image_button2("add spouse to new family", - _("Add as Spouse to New Family"), - "/family/add/spouse/%s" % obj.handle) - retval += make_image_button2("add spouse to existing family", - _("Add as Spouse to Existing Family"), - "/family/share/spouse/%s" % obj.handle) - retval += " " - retval += make_image_button2("add child to new family", - _("Add as Child to New Family"), - "/family/add/child/%s" % obj.handle) - retval += make_image_button2("add child to existing family", - _("Add as Child to Existing Family"), - "/family/share/child/%s" % obj.handle) - retval += """
""" - retval += """
""" % TAB_HEIGHT - retval += text1 + text2 + "
" - if has_data: - retval += """ \n""" % cssid - return retval - -def note_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("note_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - for reference in models.NoteRef.objects.filter(ref_object=obj): - ref_from_class = reference.object_type.model_class() - item = ref_from_class.objects.get(id=reference.object_id) - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def event_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("event_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - for reference in models.EventRef.objects.filter(ref_object=obj): - ref_from_class = reference.object_type.model_class() - try: - item = ref_from_class.objects.get(id=reference.object_id) - except: - print("Warning: Corrupt reference: %s" % reference) - continue - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def repository_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("repository_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - for reference in models.RepositoryRef.objects.filter(ref_object=obj): - ref_from_class = reference.object_type.model_class() - item = ref_from_class.objects.get(id=reference.object_id) - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def citation_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("citation_reference_table") - table.columns( - _("Type"), - _("Reference"), -# _("ID") - ) - if (user.is_authenticated() or obj.public) and act != "add": - for reference in models.CitationRef.objects.filter(citation=obj): - ref_from_class = reference.object_type.model_class() - item = ref_from_class.objects.get(id=reference.object_id) - table.row( - item.__class__.__name__, - item) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def source_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("source_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - for item in obj.citation_set.all(): - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def media_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("media_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - for reference in models.MediaRef.objects.filter(ref_object=obj): - ref_from_class = reference.object_type.model_class() - item = ref_from_class.objects.get(id=reference.object_id) - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def place_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("place_reference_table") - table.columns( - _("Type"), - _("Reference")) - if (user.is_authenticated() or obj.public) and act != "add": - # location, url, event, lds - querysets = [obj.location_set, obj.url_set, obj.event_set, obj.lds_set] - for queryset in querysets: - for item in queryset.all(): - table.row( - item.__class__.__name__, - item) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -def tag_reference_table(obj, user, act): - retval = "" - has_data = False - cssid = "tab-references" - table = Table("tag_reference_table") - table.columns( - _("Type"), - _("Reference"), - _("ID")) - if (user.is_authenticated() or obj.public) and act != "add": - querysets = [obj.person_set, obj.family_set, obj.note_set, obj.media_set] - for queryset in querysets: - for item in queryset.all(): - table.row( - item.__class__.__name__, - item, - item.gramps_id) - has_data = True - retval += table.get_html() - retval += nbsp("") # to keep tabs same height - if has_data: - retval += """ \n""" % cssid - return retval - -class Link(object): - def __init__(self, string, url=None): - self.string = string - self.url = url - def get_url(self): - return self.url - def __str__(self): - return self.string - -def children_table(obj, user, act, url=None, *args): - retval = "" - has_data = False - cssid = "tab-children" - table = Table("children_table") - table.columns( - "", - _("#"), - _("ID"), - _("Name"), - _("Gender"), - _("Paternal"), - _("Maternal"), - _("Birth Date"), - ) - table.column_widths = [11, 5, 10, 29, 8, 8, 10, 19] - - family = obj - obj_type = ContentType.objects.get_for_model(family) - childrefs = db.dji.ChildRef.filter(object_id=family.id, - object_type=obj_type).order_by("order") - links = [] - count = 1 - for childref in childrefs: - child = childref.ref_object - if user.is_authenticated() or obj.public: - table.row(Link("{{[[x%d]][[^%d]][[v%d]]}}" % (count, count, count)) if user.is_superuser and url and act == "view" else "", - str(count), - "[%s]" % child.gramps_id, - render_name(child, user), - child.gender_type, - childref.father_rel_type, - childref.mother_rel_type, - date_as_text(child.birth, user) if child.birth else "", - ) - has_data = True - links.append(('URL', childref.get_url())) - count += 1 - else: - table.row("", - str(count), - "[%s]" % child.gramps_id, - render_name(child, user) if not child.private else "[Private]", - child.gender_type if not child.private else "[Private]", - "[Private]", - "[Private]", - "[Private]", - ) - if not child.private and not childref.private: - links.append(('URL', childref.get_url())) - else: - links.append((None, None)) - has_data = True - count += 1 - table.links(links) - text = table.get_html() - retval += """
""" - if user.is_superuser and url and act == "view": - text = text.replace("{{", """
""") - text = text.replace("}}", """
""") - count = 1 - for childref in childrefs: - text = text.replace("[[x%d]]" % count, make_button("x", "/family/%s/remove/child/%d" % (family.handle, count))) - text = text.replace("[[^%d]]" % count, make_button("^", "/family/%s/up/child/%d" % (family.handle, count))) - text = text.replace("[[v%d]]" % count, make_button("v", "/family/%s/down/child/%d" % (family.handle, count))) - count += 1 - retval += make_button(_("+Add New Person as Child"), (url.replace("$act", "add") % args)) - retval += make_button(_("$Add Existing Person as Child"), (url.replace("$act", "share") % args)) - else: - retval += nbsp("") # to keep tabs same height - retval += """
""" - retval += text - if has_data: - retval += """ \n""" % cssid - return retval - -## FIXME: these dji function wrappers just use the functions -## written for the import/export. Can be done much more directly. - -def get_title(place): - if place: - return place.title - else: - return "" - -def display_date(obj): - date_tuple = db.dji.get_date(obj) - if date_tuple: - gdate = GDate() - gdate.unserialize(date_tuple) - return dd(gdate) - else: - return "" - -def media_link(handle, user, act): - retval = """""" % ( - "/media/%s/full" % handle, - "/media/%s/thumbnail" % handle) - return retval - -def render(formfield, user, act, id=None, url=None, *args): - if not user.is_authenticated(): - act = "view" - if act == "view": # show as text - fieldname = formfield.name # 'surname' - try: - item = getattr(formfield.form.model, fieldname) - if (item.__class__.__name__ == 'ManyRelatedManager'): - retval = ", ".join([i.get_link() for i in item.all()]) - else: - if url: - retval = """%s""" % (url % args, item) - elif hasattr(item, "get_link"): - retval = item.get_link() - else: - retval = str(item) - #### Some cleanup: - if fieldname == "private": # obj.private - if retval == "True": - retval = "Private" - elif retval == "False": - retval = "Not private" - else: - if retval == "True": - retval = "Yes" - elif retval == "False": - retval = "No" - except: - # name, "prefix" - try: - retval = str(formfield.form.data[fieldname]) - except: - retval = "[None]" - else: # show as widget - if id != None: - retval = formfield.as_widget(attrs={"id": id}) - else: - retval = formfield.as_widget() - if formfield.name == "private": - retval += " Private" - return retval - -def render_name(name, user, act=None): - """ - Given a Django or Gramps object, render the name and return. This - function uses authentication, privacy and probably_alive settings. - """ - if name is None: - return "[None]" - elif isinstance(name, models.Name): - if not user.is_authenticated(): - name.sanitize() - try: - surname = name.surname_set.get(primary=True) - except: - surname = "[No primary surname]" - return "%s, %s" % (surname, name.first_name) - elif isinstance(name, forms.NameForm): - if not user.is_authenticated(): - name.model.sanitize() - try: - surname = name.model.surname_set.get(primary=True) - except: - surname = "[No primary surname]" - return "%s, %s" % (surname, - name.model.first_name) - elif isinstance(name, Person): # name is a Person - person = name - try: - name = person.get_primary_name() - except: - name = None - if name is None: - return "[No preferred name]" - if not user.is_authenticated(): - name.sanitize() - try: - surname = name.surname_set.get(primary=True) - except: - surname = "[No primary surname]" - return "%s, %s" % (surname, name.first_name) - elif isinstance(name, models.Person): # django person - person = name - try: - name = person.name_set.get(preferred=True) - except: - return "Error" - return render_name(name, user) - else: # no name object - return "[No preferred name]" - -def make_name(name, user): - return render_name(name, user) - -def date_as_text(obj, user): - """ - Given a Django object, render the date as text and return. This - function uses authentication settings. - """ - if user.is_authenticated() or (obj and obj.public): - if obj: - date_tuple = db.dji.get_date(obj) - if date_tuple: - gdate = GDate().unserialize(date_tuple) - return dd(gdate) - return "" - else: - return "" - -def person_get_event(person, event_type=None): - event_ref_list = db.dji.get_event_ref_list(person) - if event_type: - index = libdjango.lookup_role_index(event_type, event_ref_list) - if index >= 0: - event_handle = event_ref_list[index][3] - # (False, [], [], 'b2cfa6cdec87392cf3b', (1, 'Primary')) - # WARNING: the same object can be referred to more than once - objs = models.EventRef.objects.filter(ref_object__handle=event_handle) - if objs.count() > 0: - return display_date(objs[0].ref_object) - else: - return "" - else: - return "" - else: - retval = [[obj.ref_object for obj in - models.EventRef.objects.filter(ref_object__handle=event_handle[3])] - for event_handle in event_ref_list] - return [j for i in retval for j in i] - -def boolean(s): - return s.lower() in ["true", "1", "yes", "y", "t"] - -def update_last_changed(obj, user): - obj.last_changed = datetime.datetime.now() - obj.last_changed_by = user - -register_plugins(GUser()) - -# works after registering plugins: -from gramps.plugins.docgen.htmldoc import HtmlDoc -from gramps.plugins.lib.libhtmlbackend import HtmlBackend, DocBackend, process_spaces -from gramps.plugins.lib.libhtml import Html - -class WebAppBackend(HtmlBackend): - SUPPORTED_MARKUP = [ - DocBackend.BOLD, - DocBackend.ITALIC, - DocBackend.UNDERLINE, - DocBackend.FONTFACE, - DocBackend.FONTSIZE, - DocBackend.FONTCOLOR, - DocBackend.HIGHLIGHT, - DocBackend.SUPERSCRIPT, - DocBackend.LINK, - ] - - STYLETAG_MARKUP = { - DocBackend.BOLD : ("", ""), - DocBackend.ITALIC : ("", ""), - DocBackend.UNDERLINE : ('', ''), - DocBackend.SUPERSCRIPT : ("", ""), - } - -### Taken from Narrated Web Report -class StyledNoteFormatter(object): - def __init__(self, database): - self.database = database - self._backend = WebAppBackend() - self._backend.build_link = self.build_link - - def format(self, note): - return self.styled_note(note.get_styledtext()) - - def styled_note(self, styledtext): - text = str(styledtext) - if not text: - return '' - s_tags = styledtext.get_tags() - markuptext = self._backend.add_markup_from_styled(text, s_tags, split='\n').replace("\n\n", "

").replace("\n", "
") - return markuptext - - def build_link(self, prop, handle, obj_class): - """ - Build a link to an item. - """ - if prop == "gramps_id": - if obj_class in self.database.get_table_names(): - obj = self.database.get_table_metadata(obj_class)["gramps_id_func"](handle) - if obj: - handle = obj.handle - else: - raise AttributeError("gramps_id '%s' not found in '%s'" % - (handle, obj_class)) - else: - raise AttributeError("invalid gramps_id lookup " + - "in table name '%s'" % obj_class) - # handle, ppl - return "/%s/%s" % (obj_class.lower(), handle) - -class WebAppParser(HTMLParser): - BOLD = 0 - ITALIC = 1 - UNDERLINE = 2 - FONTFACE = 3 - FONTSIZE = 4 - FONTCOLOR = 5 - HIGHLIGHT = 6 # background color - SUPERSCRIPT = 7 - LINK = 8 - - def __init__(self): - HTMLParser.__init__(self) - self.__text = "" - self.__tags = {} - self.__stack = [] - - def handle_data(self, data): - self.__text += data - - def push(self, pos, tag, attrs): - self.__stack.append([pos, tag, attrs]) - - def pop(self): - return self.__stack.pop() - - def handle_starttag(self, tag, attrs): - if tag == "br": - self.__text += "\n" - return - self.push(len(self.__text), tag.lower(), attrs) - - def handle_startstoptag(self, tag, attrs): - if tag == "br": - self.__text += "\n" - return - elif tag == "p": - self.__text += "\n\n" - return - else: - print("Unhandled start/stop tag '%s'" % tag) - - def handle_endtag(self, tag): - tag = tag.lower() - if tag in ["br"]: return - (start_pos, start_tag, attrs) = self.pop() - attrs = dict(attrs) - if tag != start_tag: return # skip formats - arg = None - tagtype = None - if tag == "span": - # "span": get color, font, size - if "style" in attrs: - style = attrs["style"] - if 'color' in style: - tagtype = self.FONTCOLOR - match = re.match("color:([^;]*);", style) - if match: - arg = match.groups()[0] - else: - print("Unhandled color tag: '%s'" % style) - elif 'background-color' in style: - tagtype = self.HIGHLIGHT - match = re.match("background-color:([^;]*);", style) - if match: - arg = match.groups()[0] - else: - print("Unhandled background-color tag: '%s'" % style) - elif "font-family" in style: - tagtype = self.FONTFACE - match = re.match("font-family:'([^;]*)';", style) - if match: - arg = match.groups()[0] - else: - print("Unhandled font-family tag: '%s'" % style) - elif "font-size" in style: - tagtype = self.FONTSIZE - match = re.match("font-size:([^;]*)px;", style) - if match: - arg = int(match.groups()[0]) - else: - print("Unhandled font-size tag: '%s'" % style) - else: - print("Unhandled span arg: '%s'" % attrs) - else: - print("span has no style: '%s'" % attrs) - # "b", "i", "u", "sup": direct conversion - elif tag == "b": - tagtype = self.BOLD - elif tag == "i": - tagtype = self.ITALIC - elif tag == "u": - tagtype = self.UNDERLINE - elif tag == "sup": - tagtype = self.SUPERSCRIPT - elif tag == "p": - self.__text += "\n\n" - return - elif tag == "div": - self.__text += "\n" - return - elif tag == "a": - tagtype = self.LINK - # "a": get /object/handle, or url - if "href" in attrs: - href = attrs["href"] - if href.startswith("/"): - parts = href.split("/") - arg = "gramps://%s/handle/%s" % (parts[-2].title(), parts[-1]) - else: - arg = href - else: - print("Unhandled a with no href: '%s'" % attrs) - else: - return - print("Unhandled tag: '%s'" % tag) - - if start_pos == len(self.__text): return # does nothing - key = ((tagtype, ''), arg) - if key not in self.__tags: - self.__tags[key] = [] - self.__tags[key].append((start_pos, len(self.__text))) - - def tags(self): - # [((code, ''), string/num, [(start, stop), ...]), ...] - return [(key[0], key[1], self.__tags[key]) for key in self.__tags] - - def text(self): - return self.__text - -def parse_styled_text(text): - parser = WebAppParser() - text = text.replace(" ", " ") # otherwise removes them? - parser.feed(text) - parser.close() - return (parser.text(), parser.tags()) - -def make_log(obj, log_type, last_changed_by, reason, cache): - """ - Makes a record of the changes performed. - """ - # Can also add private - last_changed = datetime.datetime.now() - log = models.Log(referenced_by=obj, - log_type=log_type, - order=0, - reason=reason, - last_changed=last_changed, - last_changed_by=last_changed_by, - cache=cache) - log.save() - -def person_get_birth_date(person): - #db = DbDjango() - #event = get_birth_or_fallback(db, db.get_person_from_handle(person.handle)) - #if event: - # return event.date - return None - -def person_get_death_date(person): - #db = DbDjango() - #event = get_death_or_fallback(db, db.get_person_from_handle(person.handle)) - #if event: - # return event.date - return None diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 03404e46a..09c131a78 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -515,21 +515,6 @@ gramps/test/test/test_util_test.py gramps/test/test_util.py gramps/test/utils_test.py # -# webapp -# -gramps/webapp/connection.py -gramps/webapp/context.py -gramps/webapp/dbdjango.py -gramps/webapp/grampsdb/admin.py -gramps/webapp/grampsdb/forms.py -gramps/webapp/grampsdb/__init__.py -gramps/webapp/grampsdb/models.py -gramps/webapp/grampsdb/profile.py -gramps/webapp/grampsdb/templatetags/__init__.py -gramps/webapp/grampsdb/templatetags/my_tags.py -gramps/webapp/grampsdb/view/__init__.py -gramps/webapp/grampsdb/view/png.py -# # Glade files # gramps/gui/glade/gramps.glade diff --git a/setup.py b/setup.py index fe13948c8..124f5eb0c 100755 --- a/setup.py +++ b/setup.py @@ -61,11 +61,6 @@ if svem_flag in sys.argv: # Die, setuptools, die. sys.argv.remove(svem_flag) -server = False -if '--server' in sys.argv: - sys.argv.remove('--server') - server = True - # check if the resourcepath option is used and store the path # this is for packagers that build out of the source tree # other options to setup.py are passed through @@ -368,15 +363,8 @@ package_gui = ['gramps.gui', 'gramps.gui.views.treemodels', 'gramps.gui.widgets', ] -package_webapp = ['gramps.webapp', - 'gramps.webapp.grampsdb', - 'gramps.webapp.grampsdb.templatetags', - 'gramps.webapp.grampsdb.view', - ] -if server: - packages = package_core + package_webapp -else: - packages = package_core + package_gui + +packages = package_core + package_gui #------------------------------------------------------------------------- # @@ -399,16 +387,11 @@ for (dirpath, dirnames, filenames) in os.walk(basedir): #we add to data_list so glade , xml, files are found, we don't need the gramps/ part package_data_core.append(dirpath[7:] + '/' + dirname + '/*.glade') package_data_core.append(dirpath[7:] + '/' + dirname + '/*.xml') + package_data_core.append('gen/utils/resource-path') package_data_gui = ['gui/glade/*.glade'] - -package_data_webapp = ['webapp/*.sql', 'webapp/grampsdb/sql/*.sql'] - -if server: - package_data = package_data_core + package_data_webapp -else: - package_data = package_data_core + package_data_gui +package_data = package_data_core + package_data_gui #------------------------------------------------------------------------- # @@ -461,18 +444,11 @@ data_files_gui.append(('share/gramps/images/hicolor/22x22/actions', ICON_22)) data_files_gui.append(('share/gramps/images/hicolor/48x48/actions', ICON_48)) data_files_gui.append(('share/gramps/images/hicolor/scalable/actions', ICON_SC)) -data_files_webapp = [] TEMPLATE_FILES = glob.glob(os.path.join('data/templates', '*.html')) -data_files_webapp.append(('share/gramps/templates', TEMPLATE_FILES)) ADMIN_FILES = glob.glob(os.path.join('data/templates/admin', '*.html')) -data_files_webapp.append(('share/gramps/templates/admin', ADMIN_FILES)) REG_FILES = glob.glob(os.path.join('data/templates/registration', '*.html')) -data_files_webapp.append(('share/gramps/templates/registration', REG_FILES)) -if server: - data_files = data_files_core + data_files_webapp -else: - data_files = data_files_core + data_files_gui +data_files = data_files_core + data_files_gui #------------------------------------------------------------------------- #