Move src/web to src/webapp to make it more clear what this dir is for

svn: r18327
This commit is contained in:
Doug Blank
2011-10-15 20:21:48 +00:00
parent f492515494
commit 0b1649b0f4
27 changed files with 6 additions and 6 deletions

38
src/webapp/Makefile Normal file
View File

@@ -0,0 +1,38 @@
# Initialize GRAMPS Django site
update: grampsdb/fixtures/initial_data.json
PYTHONPATH=../../src python manage.py syncdb
grampsdb/fixtures/initial_data.json:
PYTHONPATH=../../src python init.py > grampsdb/fixtures/initial_data.json
init_gramps:
PYTHONPATH=../../src python init_gramps.py # clear primary and secondary tables
run:
PYTHONPATH=../../src python manage.py runserver
sql:
PYTHONPATH=../../src python manage.py sqlall > gramps_sql.txt
docs:
mkdir -p docs
python manage.py graph_models grampsdb -i Person,Family,Source,Event,Repository,Place,Media,Note -o docs/primary-tables.png
python manage.py graph_models grampsdb -i Note -o docs/note-table.png
python manage.py graph_models grampsdb -i Media -o docs/media-table.png
python manage.py graph_models grampsdb -i Place -o docs/place-table.png
python manage.py graph_models grampsdb -i Repository -o docs/repository-table.png
python manage.py graph_models grampsdb -i Event -o docs/event-table.png
python manage.py graph_models grampsdb -i Source -o docs/source-table.png
python manage.py graph_models grampsdb -i Family -o docs/family-table.png
python manage.py graph_models grampsdb -i Person -o docs/person-table.png
python manage.py graph_models grampsdb -o docs/all-tables.png
python manage.py graph_models grampsdb -i Attribute,Datamap,Name,Lds,Tag,Address,Location,Url -o docs/secondary-tables.png
python manage.py 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 manage.py 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 manage.py 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
clean:
rm -f sqlite.db
rm -f *~ *.pyc *.pyo
rm -f grampsdb/fixtures/initial_data.json

0
src/webapp/__init__.py Normal file
View File

850
src/webapp/dbdjango.py Normal file
View File

@@ -0,0 +1,850 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" Implements a Db interface """
#------------------------------------------------------------------------
#
# Gramps Modules
#
#------------------------------------------------------------------------
import cPickle
import base64
import gen
import re
from gen.db import DbReadBase, DbWriteBase, DbTxn
from gen.db import (PERSON_KEY,
FAMILY_KEY,
SOURCE_KEY,
EVENT_KEY,
MEDIA_KEY,
PLACE_KEY,
REPOSITORY_KEY,
NOTE_KEY)
import Utils
from webapp.libdjango import DjangoInterface
class Cursor(object):
def __init__(self, model, func):
self.model = model
self.func = func
def __enter__(self):
return self
def __iter__(self):
return self.__next__()
def __next__(self):
for item in self.model.all():
yield (item.handle, self.func(item))
def __exit__(self, *args, **kwargs):
pass
def iter(self):
for item in self.model.all():
yield (item.handle, self.func(item))
yield None
class Bookmarks:
def get(self):
return [] # handles
class DjangoTxn(DbTxn):
def __init__(self, message, db, table=None):
DbTxn.__init__(self, message, db)
self.table = table
def get(self, key, default=None, txn=None, **kwargs):
"""
Returns the data object associated with key
"""
try:
return self.table.objects(handle=key)
except:
if txn and key in txn:
return txn[key]
else:
return None
class DbDjango(DbWriteBase, DbReadBase):
"""
A Gramps Database Backend. This replicates the grampsdb functions.
"""
def __init__(self):
DbReadBase.__init__(self)
DbWriteBase.__init__(self)
self.dji = DjangoInterface()
self.readonly = False
self.db_is_open = True
self.name_formats = []
self.bookmarks = Bookmarks()
self.family_bookmarks = Bookmarks()
self.event_bookmarks = Bookmarks()
self.place_bookmarks = Bookmarks()
self.source_bookmarks = Bookmarks()
self.repo_bookmarks = Bookmarks()
self.media_bookmarks = Bookmarks()
self.note_bookmarks = Bookmarks()
self.set_person_id_prefix('I%04d')
self.set_object_id_prefix('O%04d')
self.set_family_id_prefix('F%04d')
self.set_source_id_prefix('S%04d')
self.set_place_id_prefix('P%04d')
self.set_event_id_prefix('E%04d')
self.set_repository_id_prefix('R%04d')
self.set_note_id_prefix('N%04d')
# ----------------------------------
self.id_trans = DjangoTxn("ID Transaction", self, self.dji.Person)
self.fid_trans = DjangoTxn("FID Transaction", self, self.dji.Family)
self.pid_trans = DjangoTxn("PID Transaction", self, self.dji.Place)
self.sid_trans = DjangoTxn("SID Transaction", self, self.dji.Source)
self.oid_trans = DjangoTxn("OID Transaction", self, self.dji.Media)
self.rid_trans = DjangoTxn("RID Transaction", self, self.dji.Repository)
self.nid_trans = DjangoTxn("NID Transaction", self, self.dji.Note)
self.eid_trans = DjangoTxn("EID Transaction", self, self.dji.Event)
self.smap_index = 0
self.emap_index = 0
self.pmap_index = 0
self.fmap_index = 0
self.lmap_index = 0
self.omap_index = 0
self.rmap_index = 0
self.nmap_index = 0
self.env = None
self.person_map = {}
self.family_map = {}
self.place_map = {}
self.source_map = {}
self.repository_map = {}
self.note_map = {}
self.media_map = {}
self.event_map = {}
self.metadata = {}
self.name_group = {}
self.undo_callback = None
self.redo_callback = None
self.undo_history_callback = None
self.modified = 0
self.txn = DjangoTxn("DbDjango Transaction", self)
# Import cache for gedcom import, uses transactions, and
# two step adding of objects.
self.import_cache = {}
self.use_import_cache = False
self.use_db_cache = True
def prepare_import(self):
"""
DbDjango does not commit data on gedcom import, but saves them
for later commit.
"""
self.use_import_cache = True
self.import_cache = {}
def commit_import(self):
"""
Commits the items that were queued up during the last gedcom
import for two step adding.
"""
# First we add the primary objects:
for key in self.import_cache.keys():
obj = self.import_cache[key]
if isinstance(obj, gen.lib.Person):
self.dji.add_person(obj.serialize())
elif isinstance(obj, gen.lib.Family):
self.dji.add_family(obj.serialize())
elif isinstance(obj, gen.lib.Event):
self.dji.add_event(obj.serialize())
elif isinstance(obj, gen.lib.Place):
self.dji.add_place(obj.serialize())
elif isinstance(obj, gen.lib.Repository):
self.dji.add_repository(obj.serialize())
elif isinstance(obj, gen.lib.Source):
self.dji.add_source(obj.serialize())
elif isinstance(obj, gen.lib.Note):
self.dji.add_note(obj.serialize())
# Next we add the links:
for key in self.import_cache.keys():
obj = self.import_cache[key]
if isinstance(obj, gen.lib.Person):
self.dji.add_person_detail(obj.serialize())
elif isinstance(obj, gen.lib.Family):
self.dji.add_family_detail(obj.serialize())
elif isinstance(obj, gen.lib.Event):
self.dji.add_event_detail(obj.serialize())
elif isinstance(obj, gen.lib.Place):
self.dji.add_place_detail(obj.serialize())
elif isinstance(obj, gen.lib.Repository):
self.dji.add_repository_detail(obj.serialize())
elif isinstance(obj, gen.lib.Source):
self.dji.add_source_detail(obj.serialize())
elif isinstance(obj, gen.lib.Note):
self.dji.add_note_detail(obj.serialize())
self.use_import_cache = False
self.import_cache = {}
def transaction_commit(self, txn):
pass
def enable_signals(self):
pass
def request_rebuild(self):
self.dji.rebuild_caches()
def get_undodb(self):
return None
def transaction_abort(self, txn):
pass
@staticmethod
def _validated_id_prefix(val, default):
if isinstance(val, basestring) and val:
try:
str_ = val % 1
except TypeError: # missing conversion specifier
prefix_var = val + "%d"
except ValueError: # incomplete format
prefix_var = default+"%04d"
else:
prefix_var = val # OK as given
else:
prefix_var = default+"%04d" # not a string or empty string
return prefix_var
@staticmethod
def __id2user_format(id_pattern):
"""
Return a method that accepts a Gramps ID and adjusts it to the users
format.
"""
pattern_match = re.match(r"(.*)%[0 ](\d+)[diu]$", id_pattern)
if pattern_match:
str_prefix = pattern_match.group(1)
nr_width = pattern_match.group(2)
def closure_func(gramps_id):
if gramps_id and gramps_id.startswith(str_prefix):
id_number = gramps_id[len(str_prefix):]
if id_number.isdigit():
id_value = int(id_number, 10)
if len(str(id_value)) > nr_width:
# The ID to be imported is too large to fit in the
# users format. For now just create a new ID,
# because that is also what happens with IDs that
# are identical to IDs already in the database. If
# the problem of colliding import and already
# present IDs is solved the code here also needs
# some solution.
gramps_id = id_pattern % 1
else:
gramps_id = id_pattern % id_value
return gramps_id
else:
def closure_func(gramps_id):
return gramps_id
return closure_func
def set_person_id_prefix(self, val):
"""
Set the naming template for GRAMPS Person ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as I%d or I%04d.
"""
self.person_prefix = self._validated_id_prefix(val, "I")
self.id2user_format = self.__id2user_format(self.person_prefix)
def set_source_id_prefix(self, val):
"""
Set the naming template for GRAMPS Source ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as S%d or S%04d.
"""
self.source_prefix = self._validated_id_prefix(val, "S")
self.sid2user_format = self.__id2user_format(self.source_prefix)
def set_object_id_prefix(self, val):
"""
Set the naming template for GRAMPS MediaObject ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as O%d or O%04d.
"""
self.mediaobject_prefix = self._validated_id_prefix(val, "O")
self.oid2user_format = self.__id2user_format(self.mediaobject_prefix)
def set_place_id_prefix(self, val):
"""
Set the naming template for GRAMPS Place ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as P%d or P%04d.
"""
self.place_prefix = self._validated_id_prefix(val, "P")
self.pid2user_format = self.__id2user_format(self.place_prefix)
def set_family_id_prefix(self, val):
"""
Set the naming template for GRAMPS Family ID values. The string is
expected to be in the form of a simple text string, or in a format
that contains a C/Python style format string using %d, such as F%d
or F%04d.
"""
self.family_prefix = self._validated_id_prefix(val, "F")
self.fid2user_format = self.__id2user_format(self.family_prefix)
def set_event_id_prefix(self, val):
"""
Set the naming template for GRAMPS Event ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as E%d or E%04d.
"""
self.event_prefix = self._validated_id_prefix(val, "E")
self.eid2user_format = self.__id2user_format(self.event_prefix)
def set_repository_id_prefix(self, val):
"""
Set the naming template for GRAMPS Repository ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as R%d or R%04d.
"""
self.repository_prefix = self._validated_id_prefix(val, "R")
self.rid2user_format = self.__id2user_format(self.repository_prefix)
def set_note_id_prefix(self, val):
"""
Set the naming template for GRAMPS Note ID values.
The string is expected to be in the form of a simple text string, or
in a format that contains a C/Python style format string using %d,
such as N%d or N%04d.
"""
self.note_prefix = self._validated_id_prefix(val, "N")
self.nid2user_format = self.__id2user_format(self.note_prefix)
def __find_next_gramps_id(self, prefix, map_index, trans):
"""
Helper function for find_next_<object>_gramps_id methods
"""
index = prefix % map_index
while trans.get(str(index), txn=self.txn) is not None:
map_index += 1
index = prefix % map_index
map_index += 1
return (map_index, index)
def find_next_person_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Person object based off the
person ID prefix.
"""
self.pmap_index, gid = self.__find_next_gramps_id(self.person_prefix,
self.pmap_index, self.id_trans)
return gid
def find_next_place_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Place object based off the
place ID prefix.
"""
self.lmap_index, gid = self.__find_next_gramps_id(self.place_prefix,
self.lmap_index, self.pid_trans)
return gid
def find_next_event_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Event object based off the
event ID prefix.
"""
self.emap_index, gid = self.__find_next_gramps_id(self.event_prefix,
self.emap_index, self.eid_trans)
return gid
def find_next_object_gramps_id(self):
"""
Return the next available GRAMPS' ID for a MediaObject object based
off the media object ID prefix.
"""
self.omap_index, gid = self.__find_next_gramps_id(self.mediaobject_prefix,
self.omap_index, self.oid_trans)
return gid
def find_next_source_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Source object based off the
source ID prefix.
"""
self.smap_index, gid = self.__find_next_gramps_id(self.source_prefix,
self.smap_index, self.sid_trans)
return gid
def find_next_family_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Family object based off the
family ID prefix.
"""
self.fmap_index, gid = self.__find_next_gramps_id(self.family_prefix,
self.fmap_index, self.fid_trans)
return gid
def find_next_repository_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Respository object based
off the repository ID prefix.
"""
self.rmap_index, gid = self.__find_next_gramps_id(self.repository_prefix,
self.rmap_index, self.rid_trans)
return gid
def find_next_note_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Note object based off the
note ID prefix.
"""
self.nmap_index, gid = self.__find_next_gramps_id(self.note_prefix,
self.nmap_index, self.nid_trans)
return gid
def get_mediapath(self):
return None
def get_name_group_keys(self):
return []
def get_name_group_mapping(self, key):
return None
def get_researcher(self):
obj = gen.lib.Researcher()
return obj
def get_person_handles(self):
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_source_handles(self):
return [item.handle for item in self.dji.Source.all()]
def get_place_handles(self):
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):
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):
return []
def get_event_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
event = self.dji.Event.get(handle=handle)
except:
return None
return self.make_event(event)
def get_family_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
family = self.dji.Family.get(handle=handle)
except:
return None
return self.make_family(family)
def get_family_from_gramps_id(self, gramps_id):
if self.import_cache:
for handle in self.import_cache:
if self.import_cache[handle].gramps_id == gramps_id:
return self.import_cache[handle]
try:
family = self.dji.Family.get(gramps_id=gramps_id)
except:
return None
return self.make_family(family)
def get_repository_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
repository = self.dji.Repository.get(handle=handle)
except:
return None
return self.make_repository(repository)
def get_person_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
person = self.dji.Person.get(handle=handle)
except:
return None
return self.make_person(person)
def make_repository(self, repository):
if self.use_db_cache and repository.cache:
data = cPickle.loads(base64.decodestring(repository.cache))
else:
data = self.dji.get_repository(repository)
return gen.lib.Repository.create(data)
def make_source(self, source):
if self.use_db_cache and source.cache:
data = cPickle.loads(base64.decodestring(source.cache))
else:
data = self.dji.get_source(source)
return gen.lib.Source.create(data)
def make_family(self, family):
if self.use_db_cache and family.cache:
data = cPickle.loads(base64.decodestring(family.cache))
else:
data = self.dji.get_family(family)
return gen.lib.Family.create(data)
def make_person(self, person):
if self.use_db_cache and person.cache:
data = cPickle.loads(base64.decodestring(person.cache))
else:
data = self.dji.get_person(person)
return gen.lib.Person.create(data)
def make_event(self, event):
if self.use_db_cache and event.cache:
data = cPickle.loads(base64.decodestring(event.cache))
else:
data = self.dji.get_event(event)
return gen.lib.Event.create(data)
def make_note(self, note):
if self.use_db_cache and note.cache:
data = cPickle.loads(base64.decodestring(note.cache))
else:
data = self.dji.get_note(note)
return gen.lib.Note.create(data)
def make_place(self, place):
if self.use_db_cache and place.cache:
data = cPickle.loads(base64.decodestring(place.cache))
else:
data = self.dji.get_place(place)
return gen.lib.Place.create(data)
def make_media(self, media):
if self.use_db_cache and media.cache:
data = cPickle.loads(base64.decodestring(media.cache))
else:
data = self.dji.get_media(media)
return gen.lib.Media.create(data)
def get_place_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
# FIXME: use object cache
try:
dji_obj = self.dji.Place.get(handle=handle)
except:
dji_obj = None
if dji_obj:
tuple_obj = self.dji.get_place(dji_obj)
if tuple_obj:
obj = gen.lib.Place()
obj.unserialize(tuple_obj)
return obj
return None
def get_source_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
source = self.dji.Source.get(handle=handle)
except:
return None
return self.make_source(source)
def get_note_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
note = self.dji.Note.get(handle=handle)
except:
return None
return self.make_note(note)
def get_object_from_handle(self, handle):
if handle in self.import_cache:
return self.import_cache[handle]
try:
media = self.dji.Media.get(handle=handle)
except:
return None
return self.make_media(media)
def get_media_object_handles(self):
return [media.handle for media in self.dji.Media.all()]
def get_person_handles(self, sort_handles=False):
return [person.handle for person in self.dji.Person.all()]
def get_default_person(self):
return None
def iter_people(self):
return (self.get_person_from_handle(person.handle)
for person in self.dji.Person.all())
def iter_person_handles(self):
return (person.handle for person in self.dji.Person.all())
def iter_families(self):
return (self.get_family_from_handle(family.handle)
for family in self.dji.Family.all())
def iter_family_handles(self):
return (family.handle for family in self.dji.Family.all())
def get_person_from_gramps_id(self, gramps_id):
if self.import_cache:
for handle in self.import_cache:
if self.import_cache[handle].gramps_id == gramps_id:
return self.import_cache[handle]
match_list = self.dji.Person.filter(gramps_id=gramps_id)
if match_list.count() > 0:
return self.make_person(match_list[0])
else:
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 0 # 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_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 get_place_cursor(self):
return Cursor(self.dji.Place, self.make_place).iter()
def get_person_cursor(self):
return Cursor(self.dji.Person, self.make_person).iter()
def get_family_cursor(self):
return Cursor(self.dji.Family, self.make_family).iter()
def get_events_cursor(self):
return Cursor(self.dji.Event, self.make_event).iter()
def get_source_cursor(self):
return Cursor(self.dji.Source, self.make_source).iter()
def has_person_handle(self, handle):
return self.dji.Person.filter(handle=handle).count() == 1
def has_family_handle(self, handle):
return self.dji.Family.filter(handle=handle).count() == 1
def has_source_handle(self, handle):
return self.dji.Source.filter(handle=handle).count() == 1
def has_repository_handle(self, handle):
return self.dji.Repository.filter(handle=handle).count() == 1
def has_note_handle(self, handle):
return self.dji.Note.filter(handle=handle).count() == 1
def has_place_handle(self, handle):
return self.dji.Place.filter(handle=handle).count() == 1
def get_raw_person_data(self, handle):
# FIXME?: not cached
try:
return self.dji.get_person(self.dji.Person.get(handle=handle))
except:
return None
def get_raw_family_data(self, handle):
try:
return self.dji.get_family(self.dji.Family.get(handle=handle))
except:
return None
def get_raw_source_data(self, handle):
try:
return self.dji.get_source(self.dji.Source.get(handle=handle))
except:
return None
def get_raw_repository_data(self, handle):
try:
return self.dji.get_repository(self.dji.Repository.get(handle=handle))
except:
return None
def get_raw_note_data(self, handle):
try:
return self.dji.get_note(self.dji.Note.get(handle=handle))
except:
return None
def get_raw_place_data(self, handle):
try:
return self.dji.get_place(self.dji.Place.get(handle=handle))
except:
return None
def add_person(self, person, trans, set_gid=True):
if not person.handle:
person.handle = Utils.create_id()
if not person.gramps_id or set_gid:
person.gramps_id = self.find_next_person_gramps_id()
self.commit_person(person, trans)
return person.handle
def add_family(self, family, trans, set_gid=True):
if not family.handle:
family.handle = Utils.create_id()
if not family.gramps_id or set_gid:
family.gramps_id = self.find_next_family_gramps_id()
self.commit_family(family, trans)
return family.handle
def add_source(self, source, trans, set_gid=True):
if not source.handle:
source.handle = Utils.create_id()
if not source.gramps_id or set_gid:
source.gramps_id = self.find_next_source_gramps_id()
self.commit_source(source, trans)
return source.handle
def add_repository(self, repository, trans, set_gid=True):
if not repository.handle:
repository.handle = Utils.create_id()
if not repository.gramps_id or set_gid:
repository.gramps_id = self.find_next_repository_gramps_id()
self.commit_repository(repository, trans)
return repository.handle
def add_note(self, note, trans, set_gid=True):
if not note.handle:
note.handle = Utils.create_id()
if not note.gramps_id or set_gid:
note.gramps_id = self.find_next_note_gramps_id()
self.commit_note(note, trans)
return note.handle
def add_place(self, place, trans, set_gid=True):
if not place.handle:
place.handle = Utils.create_id()
if not place.gramps_id or set_gid:
place.gramps_id = self.find_next_place_gramps_id()
self.commit_place(place, trans)
return place.handle
def add_event(self, event, trans, set_gid=True):
if not event.handle:
event.handle = Utils.create_id()
if not event.gramps_id or set_gid:
event.gramps_id = self.find_next_event_gramps_id()
self.commit_event(event, trans)
return event.handle
def commit_person(self, person, trans, change_time=None):
self.import_cache[person.handle] = person
def commit_family(self, family, trans, change_time=None):
self.import_cache[family.handle] = family
def commit_source(self, source, trans, change_time=None):
self.import_cache[source.handle] = source
def commit_repository(self, repository, trans, change_time=None):
self.import_cache[repository.handle] = repository
def commit_note(self, note, trans, change_time=None):
self.import_cache[note.handle] = note
def commit_place(self, place, trans, change_time=None):
self.import_cache[place.handle] = place
def commit_event(self, event, trans, change_time=None):
self.import_cache[event.handle] = event
def get_gramps_ids(self, obj_key):
key2table = {
PERSON_KEY: self.id_trans,
FAMILY_KEY: self.fid_trans,
SOURCE_KEY: self.sid_trans,
EVENT_KEY: self.eid_trans,
MEDIA_KEY: self.oid_trans,
PLACE_KEY: self.pid_trans,
REPOSITORY_KEY: self.rid_trans,
NOTE_KEY: self.nid_trans,
}
table = key2table[obj_key]
return table.keys()
def transaction_begin(self, transaction):
return
def disable_signals(self):
pass
def set_researcher(self, owner):
pass

View File

View File

@@ -0,0 +1,7 @@
from web.grampsdb.models import *
from django.contrib import admin
for type_name in get_tables("all"):
admin.site.register(type_name[1])
admin.site.register(Profile)

View File

@@ -0,0 +1,64 @@
# forms.py forms for Django web project
from django import forms
from web.grampsdb.models import *
from django.forms.models import inlineformset_factory
from django.forms.models import BaseModelFormSet
from django.forms.widgets import TextInput
import datetime
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ["death", "birth", "handle", "birth_ref_index", "death_ref_index"]
surname = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
prefix = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
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:
surname = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
first_name = forms.CharField(label="Given",
required=False,
widget=TextInput(attrs={'size':'30'}))
title = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
prefix = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
suffix = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
call = forms.CharField(label="Callname",
required=False,
widget=TextInput(attrs={'size':'30'}))
patronymic = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
group_as = forms.CharField(required=False,
widget=TextInput(attrs={'size':'30'}))
text = forms.CharField(label="Date",
required=False,
widget=TextInput(attrs={'size':'30'}))
class NameFormFromPerson(NameForm):
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",
"sort_as", "display_as"]

View File

@@ -0,0 +1,915 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 B. Malengier <benny.malengier@gmail.com>
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
"""
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.
"""
_DEBUG = True
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from gen.lib.date import Date as GDate, Today
from Utils import create_id, create_uid
from web.grampsdb.profile import Profile
#---------------------------------------------------------------------------
#
# Support functions
#
#---------------------------------------------------------------------------
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])
if new and _DEBUG:
print "DEBUG: Made new type:", the_type, data
return obj
else:
return the_type.objects.get(val=data[0])
def get_default_type(the_type):
"""
Gets the default row for a given Type.
"""
val, name = the_type._DEFAULT
return the_type.objects.get(val=val, name=name)
def get_datamap(grampsclass):
return [(x[0],x[2]) for x in grampsclass._DATAMAP]
#---------------------------------------------------------------------------
#
# 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 __unicode__(self): return self.name
def get_default_type(self):
""" return a tuple default (val,name) """
return self._DATAMAP[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 gen.lib.nametype import NameType
_DATAMAP = get_datamap(NameType)
_CUSTOM = NameType._CUSTOM
_DEFAULT = _DATAMAP[NameType._DEFAULT]
val = models.IntegerField('name type', choices=_DATAMAP, blank=False)
class NameOriginType(mGrampsType):
from gen.lib.nameorigintype import NameOriginType
_DATAMAP = get_datamap(NameOriginType)
_CUSTOM = NameOriginType._CUSTOM
_DEFAULT = _DATAMAP[NameOriginType._DEFAULT]
val = models.IntegerField('name origin type', choices=_DATAMAP, blank=False)
class AttributeType(mGrampsType):
from gen.lib.attrtype import AttributeType
_DATAMAP = get_datamap(AttributeType)
_CUSTOM = AttributeType._CUSTOM
_DEFAULT = _DATAMAP[AttributeType._DEFAULT]
val = models.IntegerField('attribute type', choices=_DATAMAP, blank=False)
class UrlType(mGrampsType):
from gen.lib.urltype import UrlType
_DATAMAP = get_datamap(UrlType)
_CUSTOM = UrlType._CUSTOM
_DEFAULT = _DATAMAP[UrlType._DEFAULT]
val = models.IntegerField('url type', choices=_DATAMAP, blank=False)
class ChildRefType(mGrampsType):
from gen.lib.childreftype import ChildRefType
_DATAMAP = get_datamap(ChildRefType)
_CUSTOM = ChildRefType._CUSTOM
_DEFAULT = _DATAMAP[ChildRefType._DEFAULT]
val = models.IntegerField('child reference type', choices=_DATAMAP,
blank=False)
class RepositoryType(mGrampsType):
from gen.lib.repotype import RepositoryType
_DATAMAP = get_datamap(RepositoryType)
_CUSTOM = RepositoryType._CUSTOM
_DEFAULT = _DATAMAP[RepositoryType._DEFAULT]
val = models.IntegerField('repository type', choices=_DATAMAP, blank=False)
class EventType(mGrampsType):
from gen.lib.eventtype import EventType
_DATAMAP = get_datamap(EventType)
_CUSTOM = EventType._CUSTOM
_DEFAULT = _DATAMAP[EventType._DEFAULT]
BIRTH = 12
DEATH = 13
val = models.IntegerField('event type', choices=_DATAMAP, blank=False)
class FamilyRelType(mGrampsType):
from gen.lib.familyreltype import FamilyRelType
_DATAMAP = get_datamap(FamilyRelType)
_CUSTOM = FamilyRelType._CUSTOM
_DEFAULT = _DATAMAP[FamilyRelType._DEFAULT]
val = models.IntegerField('family relation type', choices=_DATAMAP,
blank=False)
class SourceMediaType(mGrampsType):
from gen.lib.srcmediatype import SourceMediaType
_DATAMAP = get_datamap(SourceMediaType)
_CUSTOM = SourceMediaType._CUSTOM
_DEFAULT = _DATAMAP[SourceMediaType._DEFAULT]
val = models.IntegerField('source medium type', choices=_DATAMAP,
blank=False)
class EventRoleType(mGrampsType):
from gen.lib.eventroletype import EventRoleType
_DATAMAP = get_datamap(EventRoleType)
_CUSTOM = EventRoleType._CUSTOM
_DEFAULT = _DATAMAP[EventRoleType._DEFAULT]
val = models.IntegerField('event role type', choices=_DATAMAP, blank=False)
class NoteType(mGrampsType):
from gen.lib.notetype import NoteType
_DATAMAP = get_datamap(NoteType)
_CUSTOM = NoteType._CUSTOM
_DEFAULT = _DATAMAP[NoteType._DEFAULT]
val = models.IntegerField('note type', choices=_DATAMAP, blank=False)
class MarkupType(mGrampsType):
from gen.lib.notetype import NoteType
_DATAMAP = [(0, "Custom")]
_CUSTOM = 0
_DEFAULT = _DATAMAP[0]
val = models.IntegerField('note 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[CAL_GREGORIAN]
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[MOD_NONE]
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[NEWYEAR_JAN1]
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=25)
description = models.TextField('description')
value_type = models.CharField('type of value', max_length=25)
value = models.TextField('value')
class Tag(models.Model):
handle = models.CharField(max_length=19, unique=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) # "#000000000000" # Black
priority = models.IntegerField('priority', blank=False)
# 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('gramps 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')
#attributes = models.ManyToManyField("Attribute", blank=True, null=True)
cache = models.TextField(blank=True, null=True)
def __unicode__(self): return "%s: %s" % (self.__class__.__name__,
self.gramps_id)
class Person(PrimaryObject):
"""
The model for the person object
"""
gender_type = models.ForeignKey('GenderType')
probably_alive = models.BooleanField("Probably alive")
families = models.ManyToManyField('Family', blank=True, null=True)
parent_families = models.ManyToManyField('Family',
related_name="parent_families",
blank=True, null=True)
#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)
tags = models.ManyToManyField('Tag', blank=True, null=True)
# 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 __unicode__(self):
return str(self.get_primary_name())
def make_tag_list(self):
return tuple()
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')
tags = models.ManyToManyField('Tag', blank=True, null=True)
def make_tag_list(self):
return tuple()
#lds_list = models.ManyToManyField('Lds', null=True, blank=True)
# Others keys here:
# .lds_set
class Source(PrimaryObject):
title = models.CharField(max_length=50, blank=True)
author = models.CharField(max_length=50, blank=True)
pubinfo = models.CharField(max_length=50, blank=True)
abbrev = models.CharField(max_length=50, blank=True)
#datamaps = models.ManyToManyField('Datamap', null=True, blank=True)
references = generic.GenericRelation('SourceRef', related_name="refs",
content_type_field="object_type",
object_id_field="object_id")
# Other keys here:
# .datamap_set
class Event(DateObject, PrimaryObject):
event_type = models.ForeignKey('EventType')
description = models.CharField('description', max_length=50, blank=True)
place = models.ForeignKey('Place', null=True)
references = generic.GenericRelation('EventRef', related_name="refs",
content_type_field="object_type",
object_id_field="object_id")
class Repository(PrimaryObject):
repository_type = models.ForeignKey('RepositoryType')
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)
# Others keys here:
# .address_set
# .url_set
class Place(PrimaryObject):
title = models.TextField(blank=True)
#locations = models.ManyToManyField('Location', null=True, blank=True)
long = models.TextField(blank=True)
lat = models.TextField(blank=True)
#url_list = models.ManyToManyField('Url', null=True, blank=True)
# 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(blank=True)
references = generic.GenericRelation('MediaRef', related_name="refs",
content_type_field="object_type",
object_id_field="object_id")
tags = models.ManyToManyField('Tag', blank=True, null=True)
def make_tag_list(self):
return tuple()
class Note(PrimaryObject):
note_type = models.ForeignKey('NoteType')
text = models.TextField(blank=True)
preformatted = models.BooleanField('preformatted')
references = generic.GenericRelation('NoteRef', related_name="refs",
content_type_field="object_type",
object_id_field="object_id")
tags = models.ManyToManyField('Tag', blank=True, null=True)
def make_tag_list(self):
return tuple()
#---------------------------------------------------------------------------
#
# 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()
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',
related_name="name_origin_code",
default=2)
surname = models.TextField(blank=True)
prefix = models.TextField(blank=True)
primary = models.BooleanField('Primary surname?')
connector = models.TextField(blank=True)
name = models.ForeignKey("Name")
def __unicode__(self):
return "%s" % self.surname
class Name(DateObject, SecondaryObject):
name_type = models.ForeignKey('NameType',
related_name="name_code",
default=2)
preferred = models.BooleanField('Preferred name?')
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 get_primary_surname(self):
try:
return self.surname_set.get(primary=True).surname
except:
return ""
def __unicode__(self):
return "%s, %s" % (self.get_primary_surname(),
self.first_name)
@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.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()]
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')
markup_type = models.ForeignKey('MarkupType')
order = models.PositiveIntegerField()
string = models.TextField(blank=True, null=True)
start_stop_list = models.TextField(default="[]")
class Datamap(models.Model):
key = models.CharField(max_length=80, blank=True)
value = models.CharField(max_length=80, blank=True)
source = models.ForeignKey("Source", null=True, blank=True)
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?')
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?')
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()
class NoteRef(BaseRef):
ref_object = models.ForeignKey('Note')
def __unicode__(self):
return "NoteRef to " + str(self.ref_object)
class SourceRef(DateObject, BaseRef):
ref_object = models.ForeignKey('Source')
page = models.CharField(max_length=50)
confidence = models.IntegerField()
def __unicode__(self):
return "SourceRef to " + str(self.ref_object)
class EventRef(BaseRef):
ref_object = models.ForeignKey('Event')
role_type = models.ForeignKey('EventRoleType')
def __unicode__(self):
return "EventRef to " + str(self.ref_object)
class RepositoryRef(BaseRef):
ref_object = models.ForeignKey('Repository')
source_media_type = models.ForeignKey('SourceMediaType')
call_number = models.CharField(max_length=50)
def __unicode__(self):
return "RepositoryRef to " + str(self.ref_object)
class PersonRef(BaseRef):
ref_object = models.ForeignKey('Person')
description = models.CharField(max_length=50)
def __unicode__(self):
return "PersonRef to " + str(self.ref_object)
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 __unicode__(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 __unicode__(self):
return "MediaRef to " + str(self.ref_object)
class Report(models.Model):
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)
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)
TABLES = [
("abstract", mGrampsType),
("type", NameType),
("type", NameOriginType),
("type", NameFormatType),
("type", AttributeType),
("type", UrlType),
("type", ChildRefType),
("type", RepositoryType),
("type", EventType),
("type", FamilyRelType),
("type", SourceMediaType),
("type", EventRoleType),
("type", NoteType),
("type", GenderType),
("type", LdsType),
("type", LdsStatus),
("type", ThemeType),
("abstract", DateObject),
("abstract", PrimaryObject),
("primary", Person),
("primary", Family),
("primary", Source),
("primary", Event),
("primary", Repository),
("primary", Place),
("primary", Media),
("primary", Note),
("abstract", SecondaryObject),
("secondary", Attribute),
("secondary", Datamap),
("secondary", Name),
("secondary", Surname),
("secondary", Lds),
("secondary", Markup),
("secondary", Address),
("secondary", Location),
("secondary", Url),
("abstract", BaseRef),
("ref", NoteRef),
("ref", SourceRef),
("ref", EventRef),
("ref", RepositoryRef),
("ref", PersonRef),
("ref", ChildRef),
("ref", MediaRef),
("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"]

View File

@@ -0,0 +1,46 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
class Profile(models.Model):
"""
Used to save additional information of a user, such as
themes, bookmarks, etc.
"""
user = models.ForeignKey(User, unique=True)
css_theme = models.CharField(max_length=40,
default="Web_Mainz.css")
def __unicode__(self):
return unicode(self.user)
def save_profile(sender, instance, created, **kwargs):
"""
Creates the profile when the user gets created.
"""
if created:
profile = Profile(user=instance)
profile.save()
post_save.connect(save_profile, sender=User)

View File

@@ -0,0 +1,3 @@
CREATE INDEX grampsdb_childref_object_id_object_type_id
ON grampsdb_childref (object_id, object_type_id);

View File

@@ -0,0 +1,3 @@
CREATE INDEX grampsdb_eventref_object_id_object_type_id
ON grampsdb_eventref (object_id, object_type_id);

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,3 @@
CREATE INDEX grampsdb_noteref_object_id_object_type_id
ON grampsdb_noteref (object_id, object_type_id);

View File

@@ -0,0 +1,3 @@
CREATE INDEX grampsdb_sourceref_object_id_object_type_id
ON grampsdb_sourceref (object_id, object_type_id);

View File

@@ -0,0 +1,142 @@
import re
from django import template
from django.template import escape, Library
from django.utils.safestring import mark_safe
from web.utils import *
import web.utils
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 = 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(web.utils, filter_name)
func.is_safe = True
register.filter(filter_name, func)
for tag_name in util_tags:
func = getattr(web.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)
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])
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)
missing.is_safe = True
register.filter('missing', missing)
def currentSection(view1, view2):
if 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)

View File

@@ -0,0 +1,726 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" Main view handlers """
import os
#------------------------------------------------------------------------
#
# 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, escape
from django.db.models import Q
#------------------------------------------------------------------------
#
# Gramps Modules
#
#------------------------------------------------------------------------
import web
from web.grampsdb.models import *
from web.grampsdb.forms import *
from web.dbdjango import DbDjango
import gen.proxy
from Utils import create_id
import const
_ = lambda text: text
# Views: [(<Nice name plural>, /<name>/handle, <Model>), ]
VIEWS = [(_('People'), 'person', Name),
(_('Families'), 'family', Family),
(_('Events'), 'event', Event),
(_('Notes'), 'note', Note),
(_('Media'), 'media', Media),
(_('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.
"""
context = {}
if request.user.is_authenticated():
profile = request.user.get_profile()
context["css_theme"] = profile.css_theme
else:
context["css_theme"] = "Web_Mainz.css"
# Other things for all environments:
context["gramps_version"] = const.VERSION
context["views"] = VIEWS
context["True"] = True
context["False"] = False
context["default"] = ""
return context
def main_page(request):
context = RequestContext(request)
context["view"] = 'home'
context["tview"] = _('Home')
return render_to_response("main_page.html", context)
def logout_page(request):
context = RequestContext(request)
context["view"] = 'home'
context["tview"] = _('Home')
logout(request)
# TODO: allow this once we have an error page
#if request.GET.has_key("next"):
# return redirect(request.GET.get("next"), context)
return HttpResponseRedirect('/')
def user_page(request, 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)
def fix_person(request, person):
try:
name = person.name_set.get(preferred=True)
except:
names = person.name_set.all().order_by("order")
if names.count() == 0:
name = Name(person=person,
surname_set=[Surname(surname="? Fixed")],
first_name="? Missing name",
preferred=True)
name.save()
else:
order = 1
for name in names:
if order == 1:
name.preferred = True
else:
name.preferred = False
name.order = order
name.save()
order += 1
if request:
return redirect("/person/%s" % person.handle, request)
def set_date(obj):
obj.calendar = 0
obj.modifier = 0
obj.quality = 0
obj.text = ""
obj.sortval = 0
obj.newyear = 0
obj.day1, obj.month1, obj.year1, obj.slash1 = 0, 0, 0, 0
obj.day2, obj.month2, obj.year2, obj.slash2 = 0, 0, 0, 0
def view_name_detail(request, handle, order, action="view"):
if order == "add":
order = 0
action = "add"
if request.POST.has_key("action"):
action = request.POST.get("action")
if action == "view":
person = Person.objects.get(handle=handle)
try:
name = person.name_set.filter(order=order)[0]
except:
return fix_person(request, person)
form = NameForm(instance=name)
form.model = name
elif action == "edit":
person = Person.objects.get(handle=handle)
name = person.name_set.filter(order=order)[0]
form = NameForm(instance=name)
form.model = name
elif action == "delete":
person = Person.objects.get(handle=handle)
names = person.name_set.all().order_by("order")
if names.count() > 1:
name_to_delete = names[0]
was_preferred = name_to_delete.preferred
name_to_delete.delete()
names = person.name_set.all().order_by("order")
for count in range(names[1:].count()):
if was_preferred:
names[count].preferred = True
was_preferred = False
names[count].order = count
names[count].save()
form = NameForm()
name = Name()
action = "back"
elif action == "add": # add name
person = Person.objects.get(handle=handle)
name = Name(person=person,
display_as=NameFormatType.objects.get(val=0),
sort_as=NameFormatType.objects.get(val=0),
name_type=NameType.objects.get(val=2))
form = NameForm(instance=name)
form.model = name
action = "edit"
elif action == "save":
person = Person.objects.get(handle=handle)
try:
name = person.name_set.filter(order=order)[0]
except:
order = person.name_set.count() + 1
name = Name(person=person, order=order)
form = NameForm(request.POST, instance=name)
form.model = name
if form.is_valid():
# now it is preferred:
if name.preferred: # was preferred, stil must be
form.cleaned_data["preferred"] = True
elif form.cleaned_data["preferred"]: # now is
# set all of the other names to be
# not preferred:
person.name_set.filter(~ Q(id=name.id)) \
.update(preferred=False)
# else some other name is preferred
set_date(name)
n = form.save()
else:
action = "edit"
context = RequestContext(request)
context["action"] = action
context["tview"] = _('Name')
context["view"] = 'name'
context["handle"] = handle
context["id"] = id
context["person"] = person
context["form"] = form
context["order"] = name.order
context["next"] = "/person/%s/name/%d" % (person.handle, name.order)
view_template = "view_name_detail.html"
if action == "save":
context["action"] = "view"
return redirect("/person/%s/name/%d" %
(person.handle, name.order), context)
elif action == "back":
return redirect("/person/%s/" %
(person.handle), context)
else:
return render_to_response(view_template, context)
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(file(filename))
response = HttpResponse(wrapper, mimetype=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_action(request, view, handle, action):
from web.reports import import_file
from web.reports import export_file
from cli.plug import run_report
db = DbDjango()
if view == "report":
if request.user.is_authenticated():
profile = request.user.get_profile()
report = Report.objects.get(handle=handle)
if action == "run":
args = {"off": "pdf"} # basic defaults
# override from given defaults in table:
if report.options:
for pair in str(report.options).split(" "):
if "=" in pair:
key, value = pair.split("=", 1)
args[key] = value
# override from options on webpage:
if request.GET.has_key("options"):
options = str(request.GET.get("options"))
if options:
for pair in options.split("%3D"): # from webpage
if "=" in pair:
key, value = pair.split("=", 1)
args[key] = value
if report.report_type == "textreport":
filename = "/tmp/%s-%s.%s" % (str(profile.user.username), str(handle), args["off"])
run_report(db, handle, of=filename, **args)
mimetype = 'application/%s' % args["off"]
elif report.report_type == "export":
filename = "/tmp/%s-%s.%s" % (str(profile.user.username), str(handle), args["off"])
export_file(db, filename, lambda n: n) # callback
mimetype = 'text/plain'
else:
pass # FIXME: error
return send_file(request, filename, mimetype)
# If failure, just fail for now:
context = RequestContext(request)
context["tview"] = "Results"
#context["view"] = view
#context["handle"] = handle
#context["action"] = action
context["message"] = "You need to be logged in."
#context["message"] = filename
return render_to_response("process_action.html", context)
def view_detail(request, view, handle, action="view"):
context = RequestContext(request)
context["action"] = action
context["view"] = view
if view == "event":
try:
obj = Event.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_event_detail.html'
context["tview"] = _("Event")
elif view == "family":
try:
obj = Family.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_family_detail.html'
context["tview"] = _("Family")
elif view == "media":
try:
obj = Media.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_media_detail.html'
context["tview"] = _("Media")
elif view == "note":
try:
obj = Note.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_note_detail.html'
context["tview"] = _("Note")
elif view == "person":
return view_person_detail(request, view, handle, action)
elif view == "place":
try:
obj = Place.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_place_detail.html'
context["tview"] = _("Place")
elif view == "repository":
try:
obj = Repository.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_repository_detail.html'
context["tview"] = _("Repository")
elif view == "source":
try:
obj = Source.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_source_detail.html'
context["tview"] = _("Source")
elif view == "tag":
try:
obj = Tag.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_tag_detail.html'
context["tview"] = _("Tag")
elif view == "report":
try:
obj = Report.objects.get(handle=handle)
except:
raise Http404(_("Requested %s does not exist.") % view)
view_template = 'view_report_detail.html'
context["tview"] = _("Report")
else:
raise Http404(_("Requested page type not known"))
context[view] = obj
context["next"] = "/%s/%s" % (view, obj.handle)
return render_to_response(view_template, context)
def view_person_detail(request, view, handle, action="view"):
context = RequestContext(request)
if handle == "add":
if request.POST.has_key("action"):
action = request.POST.get("action")
else:
action = "add"
elif request.POST.has_key("action"):
action = request.POST.get("action")
if request.user.is_authenticated():
if action == "edit":
# get all of the data:
person = Person.objects.get(handle=handle)
try:
name = person.name_set.get(preferred=True)
except:
name = Name(person=person, preferred=True)
pf = PersonForm(instance=person)
pf.model = person
nf = NameForm(instance=name)
nf.model = name
elif action == "add":
# make new data:
person = Person()
name = Name(person=person, preferred=True,
display_as=NameFormatType.objects.get(val=0),
sort_as=NameFormatType.objects.get(val=0),
name_type=NameType.objects.get(val=2))
nf = NameForm(instance=name)
nf.model = name
pf = PersonForm(instance=person)
pf.model = person
action = "edit"
elif action == "save":
try:
person = Person.objects.get(handle=handle)
except:
person = Person(handle=create_id())
if person.id: # editing
name = person.name_set.get(preferred=True)
else: # adding a new person with new name
name = Name(person=person, preferred=True)
pf = PersonForm(request.POST, instance=person)
pf.model = person
nf = NameFormFromPerson(request.POST, instance=name)
nf.model = name
if nf.is_valid() and pf.is_valid():
person = pf.save()
name = nf.save(commit=False)
name.person = person
name.save()
else:
action = "edit"
else: # view
person = Person.objects.get(handle=handle)
try:
name = person.name_set.get(preferred=True)
except:
return fix_person(request, person)
pf = PersonForm(instance=person)
pf.model = person
nf = NameForm(instance=name)
nf.model = name
else: # view person detail
# BEGIN NON-AUTHENTICATED ACCESS
person = Person.objects.get(handle=handle)
if person:
if person.private:
raise Http404(_("Requested %s is not accessible.") % view)
name = person.name_set.get(preferred=True)
if person.probably_alive:
name.sanitize()
else:
raise Http404(_("Requested %s does not exist.") % view)
pf = PersonForm(instance=person)
pf.model = person
nf = NameForm(instance=name)
nf.model = name
# END NON-AUTHENTICATED ACCESS
if action == "save":
context["action"] = "view"
return redirect("/person/%s" % person.handle, context)
context["action"] = action
context["view"] = view
context["tview"] = _("Person")
context["personform"] = pf
context["nameform"] = nf
context["person"] = person
context["next"] = "/person/%s" % person.handle
view_template = 'view_person_detail.html'
return render_to_response(view_template, context)
def view(request, view):
search = ""
if view == "event":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Event.objects \
.filter((Q(gramps_id__icontains=search) |
Q(event_type__name__icontains=search) |
Q(place__title__icontains=search)) &
private
) \
.order_by("gramps_id")
else:
object_list = Event.objects.filter(private).order_by("gramps_id")
view_template = 'view_events.html'
total = Event.objects.all().count()
elif view == "family":
if request.user.is_authenticated():
if request.GET.has_key("search"):
search = request.GET.get("search")
if "," in search:
surname, first = [term.strip() for term in
search.split(",", 1)]
object_list = Family.objects \
.filter((Q(father__name__surname__surname__istartswith=surname) &
Q(father__name__first_name__istartswith=first)) |
(Q(mother__name__surname__surname__istartswith=surname) &
Q(mother__name__first_name__istartswith=first))
) \
.order_by("gramps_id")
else: # no comma
object_list = Family.objects \
.filter(Q(gramps_id__icontains=search) |
Q(family_rel_type__name__icontains=search) |
Q(father__name__surname__surname__istartswith=search) |
Q(father__name__first_name__istartswith=search) |
Q(mother__name__surname__surname__istartswith=search) |
Q(mother__name__first_name__istartswith=search)
) \
.order_by("gramps_id")
else: # no search
object_list = Family.objects.all().order_by("gramps_id")
else:
# NON-AUTHENTICATED users
if request.GET.has_key("search"):
search = request.GET.get("search")
if "," in search:
search_text, trash = [term.strip() for term in search.split(",", 1)]
else:
search_text = search
object_list = Family.objects \
.filter((Q(gramps_id__icontains=search_text) |
Q(family_rel_type__name__icontains=search_text) |
Q(father__name__surname__surname__istartswith=search_text) |
Q(mother__name__surname__surname__istartswith=search_text)) &
Q(private=False) &
Q(mother__private=False) &
Q(father__private=False)
) \
.order_by("gramps_id")
else:
object_list = Family.objects \
.filter(Q(private=False) &
Q(mother__private=False) &
Q(father__private=False)
) \
.order_by("gramps_id")
view_template = 'view_families.html'
total = Family.objects.all().count()
elif view == "media":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Media.objects \
.filter(Q(gramps_id__icontains=search) &
private
) \
.order_by("gramps_id")
else:
object_list = Media.objects.filter(private).order_by("gramps_id")
view_template = 'view_media.html'
total = Media.objects.all().count()
elif view == "note":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Note.objects \
.filter((Q(gramps_id__icontains=search) |
Q(note_type__name__icontains=search) |
Q(text__icontains=search)) &
private
) \
.order_by("gramps_id")
else:
object_list = Note.objects.filter(private).order_by("gramps_id")
view_template = 'view_notes.html'
total = Note.objects.all().count()
elif view == "person":
if request.user.is_authenticated():
if request.GET.has_key("search"):
search = request.GET.get("search")
if "," in search:
surname, first_name = [term.strip() for term in
search.split(",", 1)]
object_list = Name.objects \
.filter(Q(surname__surname__istartswith=surname,
first_name__istartswith=first_name)) \
.order_by("surname__surname", "first_name")
else:
object_list = Name.objects \
.filter((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))
) \
.order_by("surname__surname", "first_name")
else:
object_list = Name.objects.all().order_by("surname__surname", "first_name")
else:
# BEGIN NON-AUTHENTICATED users
if request.GET.has_key("search"):
search = request.GET.get("search")
if "," in search:
search_text, trash = [term.strip() for term in search.split(",", 1)]
else:
search_text = search
object_list = Name.objects \
.select_related() \
.filter(Q(surname__surname__istartswith=search_text) &
Q(private=False) &
Q(person__private=False)
) \
.order_by("surname__surname", "first_name")
else:
object_list = Name.objects \
.select_related() \
.filter(Q(private=False) &
Q(person__private=False)) \
.order_by("surname__surname", "first_name")
# END NON-AUTHENTICATED users
view_template = 'view_people.html'
total = Name.objects.all().count()
elif view == "place":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Place.objects \
.filter((Q(gramps_id__icontains=search) |
Q(title__icontains=search)
) &
private
) \
.order_by("gramps_id")
else:
object_list = Place.objects.filter(private).order_by("gramps_id")
view_template = 'view_places.html'
total = Place.objects.all().count()
elif view == "repository":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Repository.objects \
.filter((Q(gramps_id__icontains=search) |
Q(name__icontains=search) |
Q(repository_type__name__icontains=search)
) &
private
) \
.order_by("gramps_id")
else:
object_list = Repository.objects.filter(private).order_by("gramps_id")
view_template = 'view_repositories.html'
total = Repository.objects.all().count()
elif view == "source":
if request.user.is_authenticated():
private = Q()
else:
# NON-AUTHENTICATED users
private = Q(private=False)
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Source.objects \
.filter(Q(gramps_id__icontains=search) &
private
) \
.order_by("gramps_id")
else:
object_list = Source.objects.filter(private).order_by("gramps_id")
view_template = 'view_sources.html'
total = Source.objects.all().count()
elif view == "tag":
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Tag.objects \
.filter(Q(name__icontains=search)) \
.order_by("name")
else:
object_list = Tag.objects.order_by("name")
view_template = 'view_tags.html'
total = Tag.objects.all().count()
elif view == "report":
if request.GET.has_key("search"):
search = request.GET.get("search")
object_list = Report.objects \
.filter(Q(name__icontains=search)) \
.order_by("name")
else:
object_list = Report.objects.all().order_by("name")
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 = RequestContext(request)
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)

170
src/webapp/init.py Normal file
View File

@@ -0,0 +1,170 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
"""
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"] = "settings"
import settings
from gen.lib.nametype import NameType
from gen.lib.nameorigintype import NameOriginType
from gen.lib.attrtype import AttributeType
from gen.lib.urltype import UrlType
from gen.lib.childreftype import ChildRefType
from gen.lib.repotype import RepositoryType
from gen.lib.eventtype import EventType
from gen.lib.familyreltype import FamilyRelType
from gen.lib.srcmediatype import SourceMediaType
from gen.lib.eventroletype import EventRoleType
from gen.lib.notetype import NoteType
from 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", "\"db_version\""),
("description", "\"database scheme version\""),
("value_type", "\"str\""),
("value", "\"0.5.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"'),
("handle", '"ancestor_report"'),
("report_type", '"textreport"')),
(("name", '"birthday_report"'),
("handle", '"birthday_report"'),
("report_type", '"textreport"')),
(("name", '"custom_text"'),
("handle", '"custom_text"'),
("report_type", '"textreport"')),
(("name", '"descend_report"'),
("handle", '"descend_report"'),
("report_type", '"textreport"')),
(("name", '"det_ancestor_report"'),
("handle", '"det_ancestor_report"'),
("report_type", '"textreport"')),
(("name", '"det_descendant_report"'),
("handle", '"det_descendant_report"'),
("report_type", '"textreport"')),
(("name", '"endofline_report"'),
("handle", '"endofline_report"'),
("report_type", '"textreport"')),
(("name", '"family_group"'),
("handle", '"family_group"'),
("report_type", '"textreport"')),
(("name", '"indiv_complete"'),
("handle", '"indiv_complete"'),
("report_type", '"textreport"')),
(("name", '"kinship_report"'),
("handle", '"kinship_report"'),
("report_type", '"textreport"')),
(("name", '"tag_report"'),
("handle", '"tag_report"'),
("report_type", '"textreport"')),
(("name", '"number_of_ancestors_report"'),
("handle", '"number_of_ancestors_report"'),
("report_type", '"textreport"')),
(("name", '"place_report"'),
("handle", '"place_report"'),
("report_type", '"textreport"')),
(("name", '"simple_book_title"'),
("handle", '"simple_book_title"'),
("report_type", '"textreport"')),
(("name", '"summary"'),
("handle", '"summary"'),
("report_type", '"textreport"')),
(("name", '"GEDCOM Export"'),
("handle", '"gedcom_export"'),
("options", '"off=ged"'),
("report_type", '"export"')),
(("name", '"Gramps XML Export"'),
("handle", '"ex_gpkg"'),
("options", '"off=gramps"'),
("report_type", '"export"')),
])]:
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)),
key_count += 1
if key_count < len(entry):
print ","
else:
print
print " }"
print " },"
entry_count += 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]
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 " }",
# 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 "]"

32
src/webapp/init_gramps.py Normal file
View File

@@ -0,0 +1,32 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
"""
Clears gramps data
"""
import os
os.environ["DJANGO_SETTINGS_MODULE"] = "settings"
import settings
import grampsdb.models as dj
dj.clear_tables("primary", "secondary", "ref", "system")

1484
src/webapp/libdjango.py Normal file

File diff suppressed because it is too large Load Diff

40
src/webapp/manage.py Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" 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)

74
src/webapp/reports.py Normal file
View File

@@ -0,0 +1,74 @@
# imports for import/export:
import DbState
from cli.grampscli import CLIManager
from gen.plug import BasePluginManager
import os
# Example for running a report:
# ------------------------------
# from cli.plug import run_report
# from django.conf import settings
# import web.settings as default_settings
# try:
# settings.configure(default_settings)
# except:
# pass
# import dbdjango
# db = dbdjango.DbDjango()
# run_report(db, "ancestor_report", off="txt", of="ar.txt", pid="I0363")
def import_file(db, filename, callback):
"""
Import a file (such as a GEDCOM file) into the given db.
>>> import_file(DbDjango(), "/home/user/Untitled_1.ged", lambda a: a)
"""
dbstate = DbState.DbState()
climanager = CLIManager(dbstate, False) # 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 name, error_tuple in pmgr.get_fail_list():
etype, exception, traceback = error_tuple
print "ERROR:", name, exception
return False
import_function = getattr(mod, pdata.import_function)
db.prepare_import()
import_function(db, filename, callback)
db.commit_import()
return True
return False
def export_file(db, filename, callback):
"""
Export the db to a file (such as a GEDCOM file).
>>> export_file(DbDjango(), "/home/user/Untitled_1.ged", lambda a: a)
"""
dbstate = DbState.DbState()
climanager = CLIManager(dbstate, False) # 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 name, error_tuple in pmgr.get_fail_list():
etype, exception, traceback = error_tuple
print "ERROR:", name, exception
return False
export_function = getattr(mod, pdata.export_function)
export_function(db, filename, callback)
return True
return False

142
src/webapp/settings.py Normal file
View File

@@ -0,0 +1,142 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" Django settings for gramps project. """
# Need to be able to import Gramps files from here.
import const
import os
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(const.WEB_DIR, 'sqlite.db'),
}
}
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = os.path.join(const.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.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',
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
)
ROOT_URLCONF = 'web.urls'
TEMPLATE_DIRS = (
# Use absolute paths, not relative paths.
os.path.join(const.DATA_DIR, "templates"),
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
# "django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"web.grampsdb.views.context_processor",
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'web.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()
# In versions < 2.7 python does not properly copy methods when doing a
# deepcopy. This workaround makes the copy work properly. When Gramps no longer
# supports python 2.6, this workaround can be removed.
import sys
if sys.version_info < (2, 7) :
import copy
import types
def _deepcopy_method(x, memo):
return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class)
copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method

229
src/webapp/sortheaders.py Normal file
View File

@@ -0,0 +1,229 @@
# Author: insin
# Site: http://www.djangosnippets.org/snippets/308/
from django.core.paginator import InvalidPage, EmptyPage
from collections import defaultdict
from unicodedata import normalize
import locale
ORDER_VAR = 'o'
ORDER_TYPE_VAR = 'ot'
def first_letter(text):
"""
Text should be a unicode string. Returns first letter.
"""
if len(text) > 0:
letter = normalize('NFKC', text)[0].upper()
(lang_country, modifier ) = locale.getlocale()
if lang_country == "sv_SE" and letter in [u'W', u'V']:
letter = u'V,W'
return letter
else:
return u'?'
class SortHeaders:
"""
Handles generation of an argument for the Django ORM's
``order_by`` method and generation of table headers which reflect
the currently selected sort, based on defined table headers with
matching sort criteria.
Based in part on the Django Admin application's ``ChangeList``
functionality.
"""
def __init__(self, request, headers, default_order_field=None,
default_order_type='asc', additional_params=None):
"""
request
The request currently being processed - the current sort
order field and type are determined based on GET
parameters.
headers
A list of two-tuples of header text and matching ordering
criteria for use with the Django ORM's ``order_by``
method. A criterion of ``None`` indicates that a header
is not sortable.
default_order_field
The index of the header definition to be used for default
ordering and when an invalid or non-sortable header is
specified in GET parameters. If not specified, the index
of the first sortable header will be used.
default_order_type
The default type of ordering used - must be one of
``'asc`` or ``'desc'``.
additional_params:
Query parameters which should always appear in sort links,
specified as a dictionary mapping parameter names to
values. For example, this might contain the current page
number if you're sorting a paginated list of items.
"""
if default_order_field is None:
for i, (header, query_lookup) in enumerate(headers):
if query_lookup is not None:
default_order_field = i
break
if default_order_field is None:
raise AttributeError('No default_order_field was specified and none of the header definitions given were sortable.')
if default_order_type not in ('asc', 'desc'):
raise AttributeError('If given, default_order_type must be one of \'asc\' or \'desc\'.')
if additional_params is None: additional_params = {}
self.header_defs = headers
self.additional_params = additional_params
self.order_field, self.order_type = default_order_field, default_order_type
# Determine order field and order type for the current request
params = dict(request.GET.items())
if ORDER_VAR in params:
try:
new_order_field = int(params[ORDER_VAR])
if headers[new_order_field][1] is not None:
self.order_field = new_order_field
except (IndexError, ValueError):
pass # Use the default
if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
self.order_type = params[ORDER_TYPE_VAR]
def headers(self):
"""
Generates dicts containing header and sort link details for
all defined headers.
"""
for i, (header, order_criterion) in enumerate(self.header_defs):
th_classes = []
new_order_type = 'asc'
if i == self.order_field:
th_classes.append('sorted %sending' % self.order_type)
new_order_type = {'asc': 'desc', 'desc': 'asc'}[self.order_type]
yield {
'text': header,
'sortable': order_criterion is not None,
'url': self.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
'class_attr': (th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
}
def get_query_string(self, params):
"""
Creates a query string from the given dictionary of
parameters, including any additonal parameters which should
always be present.
"""
params.update(self.additional_params)
return '?%s' % '&amp;'.join(['%s=%s' % (param, value)
for param, value in params.items()])
def get_order_by(self):
"""
Creates an ordering criterion based on the current order
field and order type, for use with the Django ORM's
``order_by`` method.
"""
return '%s%s' % (
self.order_type == 'desc' and '-' or '',
self.header_defs[self.order_field][1],
)
class NamePaginator(object):
"""Pagination for string-based objects"""
def __init__(self, object_list, on=None, per_page=25):
self.object_list = object_list
self.count = len(object_list)
self.pages = []
# chunk up the objects so we don't need to iterate over the whole list for each letter
chunks = defaultdict(list)
for obj in self.object_list:
if on:
obj_str = unicode(getattr(obj, on))
else:
obj_str = unicode(obj)
letter = first_letter(obj_str[0])
chunks[letter].append(obj)
# the process for assigning objects to each page
current_page = NamePage(self)
for letter in string.ascii_uppercase:
if letter not in chunks:
current_page.add([], letter)
continue
sub_list = chunks[letter] # the items in object_list starting with this letter
new_page_count = len(sub_list) + current_page.count
# first, check to see if sub_list will fit or it needs to go onto a new page.
# if assigning this list will cause the page to overflow...
# and an underflow is closer to per_page than an overflow...
# and the page isn't empty (which means len(sub_list) > per_page)...
if (new_page_count > per_page and
abs(per_page - current_page.count) < abs(per_page - new_page_count) and
current_page.count > 0):
# make a new page
self.pages.append(current_page)
current_page = NamePage(self)
current_page.add(sub_list, letter)
# if we finished the for loop with a page that isn't empty, add it
if current_page.count > 0: self.pages.append(current_page)
def page(self, num):
"""Returns a Page object for the given 1-based page number."""
if len(self.pages) == 0:
return None
elif num > 0 and num <= len(self.pages):
return self.pages[num-1]
else:
raise InvalidPage
@property
def num_pages(self):
"""Returns the total number of pages"""
return len(self.pages)
class NamePage(object):
def __init__(self, paginator):
self.paginator = paginator
self.object_list = []
self.letters = []
@property
def count(self):
return len(self.object_list)
@property
def start_letter(self):
if len(self.letters) > 0:
self.letters.sort(key=locale.strxfrm)
return first_letter(self.letters)
else: return None
@property
def end_letter(self):
if len(self.letters) > 0:
self.letters.sort(key=locale.strxfrm)
return self.letters[-1]
else: return None
@property
def number(self):
return self.paginator.pages.index(self) + 1
def add(self, new_list, letter=None):
if len(new_list) > 0: self.object_list = self.object_list + new_list
if letter: self.letters.append(letter)
def __unicode__(self):
if self.start_letter == self.end_letter:
return self.start_letter
else:
return u'%c-%c' % (self.start_letter, self.end_letter)

99
src/webapp/urls.py Normal file
View File

@@ -0,0 +1,99 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" Url handler """
#------------------------------------------------------------------------
#
# Python Modules
#
#------------------------------------------------------------------------
import os
#------------------------------------------------------------------------
#
# Django and Gramps Modules
#
#------------------------------------------------------------------------
import const
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()
from web.grampsdb.views import (main_page, user_page, logout_page,
process_action, view, view_detail,
view_name_detail)
urlpatterns = patterns('',
# Specific matches first:
(r'^admin/(.*)', admin.site.root),
)
urlpatterns += patterns('',
# Static serves! DANGEROUS in production:
(r'^styles/(?P<path>.*)$', 'django.views.static.serve',
{'document_root':
os.path.join(const.ROOT_DIR, "plugins", "webstuff"),
'show_indexes': True},
),
(r'^images/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': const.IMAGE_DIR,
'show_indexes': True},
),
)
# The rest will match views:
urlpatterns += patterns('',
(r'^$', main_page),
(r'^user/(\w+)/$', user_page),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', logout_page),
(r'^(?P<view>(\w+))/$', view),
url(r'^person/(?P<handle>(\w+))/$', view_detail,
{"view": "person"}, name="view-person-detail"),
url(r'^person/(?P<handle>(\w+))/(?P<action>(\w+))$', view_detail,
{"view": "person"}, name="view-person-detail"),
(r'^(?P<view>(\w+))/(?P<handle>(\w+))/$', view_detail),
(r'^person/(?P<handle>(\w+))/name/(?P<order>(\w+))$', view_name_detail),
(r'^person/(?P<handle>(\w+))/name/(?P<order>(\w+))/(?P<action>(\w+))$', view_name_detail),
(r'^(?P<view>(\w+))/(?P<handle>(\w+))/(?P<action>(\w+))$', process_action),
(r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/styles/images/favicon.ico'}),
)
# 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 %}
# <a href="{{ the_url }}">Link to optional stuff</a>
#{% endif %}
# In code:
#from django.core.urlresolvers import reverse
#
#def myview(request):
# return HttpResponseRedirect(reverse('arch-summary', args=[1945]))

612
src/webapp/utils.py Normal file
View File

@@ -0,0 +1,612 @@
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#
""" Django/Gramps utilities """
#------------------------------------------------------------------------
#
# Python Modules
#
#------------------------------------------------------------------------
import locale
import sys
#------------------------------------------------------------------------
#
# Django Modules
#
#------------------------------------------------------------------------
from django.template import escape
from django.utils.safestring import mark_safe
from django.contrib.contenttypes.models import ContentType
#------------------------------------------------------------------------
#
# Gramps-Connect Modules
#
#------------------------------------------------------------------------
import web.grampsdb.models as models
import web.grampsdb.forms as forms
from web import libdjango
from web.dbdjango import DbDjango
#------------------------------------------------------------------------
#
# Gramps Modules
#
#------------------------------------------------------------------------
from Simple import SimpleTable, SimpleAccess, make_basic_stylesheet
import Utils
import DbState
import DateHandler
from gen.lib.date import Date as GDate, Today
import gen.lib
from gen.utils import get_birth_or_fallback, get_death_or_fallback
from gen.plug import BasePluginManager
from cli.grampscli import CLIManager
_ = lambda msg: msg
util_filters = [
'nbsp',
'render_date',
'render_name',
]
util_tags = [
'render',
"get_person_from_handle",
"event_table",
"name_table",
"source_table",
"note_table",
"attribute_table",
"address_table",
"gallery_table",
"internet_table",
"association_table",
"lds_table",
"reference_table",
"children_table",
"make_button",
]
#------------------------------------------------------------------------
#
# Module Constants
#
#------------------------------------------------------------------------
dji = libdjango.DjangoInterface()
_dd = DateHandler.displayer.display
_dp = DateHandler.parser.parse
def register_plugins():
dbstate = DbState.DbState()
climanager = CLIManager(dbstate, False) # 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 >> sys.stderr, "error in get_person_from_handle:"
import sys, traceback
cla, exc, trbk = sys.exc_info()
print >> sys.stderr, _("Error") + (" : %s %s" %(cla, exc))
traceback.print_exc()
return None
def probably_alive(handle):
return False
db = DbDjango()
person = db.get_person_from_handle(handle)
return Utils.probably_alive(person, db)
def format_number(number, with_grouping=True):
# FIXME: should be user's setting
locale.setlocale(locale.LC_ALL, "en_US.utf8")
return locale.format("%d", number, with_grouping)
def nbsp(string):
"""
"""
if string:
return string
else:
return mark_safe("&nbsp;")
class Table(object):
"""
>>> table = Table()
>>> table.columns("Col1", "Col2", "Col3")
>>> table.row("1", "2", "3")
>>> table.row("4", "5", "6")
>>> table.get_html()
"""
def __init__(self):
self.db = DbDjango()
self.access = SimpleAccess(self.db)
self.table = SimpleTable(self.access)
class Doc(object):
def __init__(self, doc):
self.doc = doc
# None is paperstyle, which is ignored:
self.doc = Doc(HtmlDoc.HtmlDoc(make_basic_stylesheet(Table={"set_width":95}), None))
self.doc.doc._backend = HtmlBackend()
# You can set elements id, class, etc:
self.doc.doc.htmllist += [Html('div', class_="content", id="Gallery", style="overflow: auto; height:150px; background-color: #261803;")]
def columns(self, *args):
self.table.columns(*args)
def row(self, *args):
self.table.row(*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):
# The HTML writer escapes data:
self.table.write(self.doc) # forces to htmllist
# We have a couple of HTML bits that we want to unescape:
return str(self.doc.doc.htmllist[0]).replace("&amp;nbsp;", "&nbsp;")
_ = lambda text: text
def make_button(text, url, *args):
url = url % args
return """[ <a href="%s">%s</a> ] """ % (url, text)
def event_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Description"),
_("Type"),
_("ID"),
_("Date"),
_("Place"),
_("Role"))
if user.is_authenticated():
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 = [(obj.ref_object, obj) for obj in event_ref_list]
for (djevent, event_ref) in event_list:
table.row(
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))
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add event"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def name_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Name"),
_("Type"),
_("Group As"),
_("Source"),
_("Note Preview"))
if user.is_authenticated():
links = []
for name in obj.name_set.all().order_by("order"):
obj_type = ContentType.objects.get_for_model(name)
sourceq = dji.SourceRef.filter(object_type=obj_type,
object_id=name.id).count() > 0
note_refs = dji.NoteRef.filter(object_type=obj_type,
object_id=name.id)
note = ""
if note_refs.count() > 0:
try:
note = 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"][sourceq],
note)
links.append(('URL',
# url is "/person/%s/name"
(url % name.person.handle) + ("/%s" % name.order)))
table.links(links)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add name"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def source_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("ID"),
_("Title"),
_("Author"),
_("Page"))
if user.is_authenticated():
obj_type = ContentType.objects.get_for_model(obj)
source_refs = dji.SourceRef.filter(object_type=obj_type,
object_id=obj.id)
for source_ref in source_refs:
source = table.db.get_source_from_handle(source_ref.ref_object.handle)
table.row(source,
source_ref.ref_object.title,
source_ref.ref_object.author,
source_ref.page,
)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add source"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def note_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(
_("ID"),
_("Type"),
_("Note"))
if user.is_authenticated():
obj_type = ContentType.objects.get_for_model(obj)
note_refs = dji.NoteRef.filter(object_type=obj_type,
object_id=obj.id)
for note_ref in note_refs:
note = table.db.get_note_from_handle(
note_ref.ref_object.handle)
table.row(table.db.get_note_from_handle(note.handle),
str(note_ref.ref_object.note_type),
note_ref.ref_object.text[:50])
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add note"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def attribute_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Type"),
_("Value"),
)
if user.is_authenticated():
obj_type = ContentType.objects.get_for_model(obj)
attributes = dji.Attribute.filter(object_type=obj_type,
object_id=obj.id)
for attribute in attributes:
table.row(attribute.attribute_type.name,
attribute.value)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add attribute"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def address_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Date"),
_("Address"),
_("City"),
_("State"),
_("Country"))
if user.is_authenticated():
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)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add address"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def gallery_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Name"),
_("Type"),
)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add gallery"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def internet_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Type"),
_("Path"),
_("Description"))
if user.is_authenticated():
urls = dji.Url.filter(person=obj)
for url_obj in urls:
table.row(str(url_obj.url_type),
url_obj.path,
url_obj.desc)
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add internet"), ((str(url) % args) + "/add"))
else:
retval += nbsp("") # to keep tabs same height
return retval
def association_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Name"),
_("ID"),
_("Association"))
if user.is_authenticated():
gperson = table.db.get_person_from_handle(obj.handle)
if gperson:
associations = gperson.get_person_ref_list()
for association in associations:
table.row()
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add association"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def lds_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Type"),
_("Date"),
_("Status"),
_("Temple"),
_("Place"))
if user.is_authenticated():
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))
retval += table.get_html()
if user.is_authenticated() and url and action == "view":
retval += make_button(_("Add LDS"), (url + "/add") % args)
else:
retval += nbsp("") # to keep tabs same height
return retval
def reference_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(_("Type"),
_("ID"),
_("Name"))
if user.is_authenticated():
references = dji.PersonRef.filter(ref_object=obj)
for reference in references:
table.row(str(reference.ref_object),
reference.ref_object.gramps_id,
make_name(reference.ref_object.name_set, user))
retval += table.get_html()
retval += nbsp("") # to keep tabs same height
return retval
def children_table(obj, user, action, url=None, *args):
retval = ""
table = Table()
table.columns(
_("#"),
_("ID"),
_("Name"),
_("Gender"),
_("Paternal"),
_("Maternal"),
_("Birth Date"),
)
family = obj
obj_type = ContentType.objects.get_for_model(family)
childrefs = 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():
table.row(str(count),
"[%s]" % child.gramps_id,
render_name(child, user),
child.gender_type,
childref.father_rel_type,
childref.mother_rel_type,
render_date(child.birth, user),
)
links.append(('URL', ("/person/%s" % child.handle)))
else:
table.row(str(count),
"[%s]" % child.gramps_id,
render_name(child, user),
child.gender_type,
"[Private]",
"[Private]",
"[Private]",
)
links.append(('URL', ("/person/%s" % child.handle)))
count += 1
table.links(links)
retval += table.get_html()
retval += nbsp("") # to keep tabs same height
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 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
def display_date(obj):
date_tuple = dji.get_date(obj)
if date_tuple:
gdate = GDate()
gdate.unserialize(date_tuple)
return _dd(gdate)
else:
return ""
def render(formfield, user, action, test=False, truetext=""):
if not user.is_authenticated():
action = "view"
if action == "view":
if (not user.is_authenticated() and not test) or user.is_authenticated():
fieldname = formfield.name # 'surname'
retval = str(getattr(formfield.form.model, fieldname))
else:
retval = truetext
else:
retval = formfield.as_widget()
return retval
def render_name(name, user):
"""
Given a Django or Gramps object, render the name and return. This
function uses authentication, privacy and probably_alive settings.
"""
if isinstance(name, models.Name):
if not user.is_authenticated():
name.sanitize()
return "%s, %s" % (name.get_primary_surname(), name.first_name)
elif isinstance(name, forms.NameForm):
if not user.is_authenticated():
name.model.sanitize()
return "%s, %s" % (name.model.get_primary_surname(),
name.model.first_name)
elif isinstance(name, gen.lib.Person): # name is a gen.lib.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()
return "%s, %s" % (name.get_primary_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 render_date(obj, user):
"""
Given a Django object, render the date as text and return. This
function uses authentication settings.
"""
if (user.is_authenticated() or
(not user.is_authenticated() and obj and not obj.private)):
if obj:
date_tuple = dji.get_date(obj)
if date_tuple:
gdate = GDate().unserialize(date_tuple)
return _dd(gdate)
return ""
return "[Private]"
def person_get_event(person, event_type=None):
event_ref_list = 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, [], [], u'b2cfa6cdec87392cf3b', (1, u'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]
register_plugins()
# works after registering plugins:
import HtmlDoc
from libhtmlbackend import HtmlBackend
from libhtml import Html