diff --git a/src/data/templates/gramps-base.html b/src/data/templates/gramps-base.html new file mode 100644 index 000000000..88091cad0 --- /dev/null +++ b/src/data/templates/gramps-base.html @@ -0,0 +1,55 @@ +{% load my_tags %} + + + + + {% block title %}GRAMPS Connect{% endblock %} + {% block meta %} + + + + + {% endblock %} + + {% block css %} + + + + + {% endblock %} + + + + +
+
+ {% block content %} + {% endblock %} +
+
+ + + diff --git a/src/data/templates/main_page.html b/src/data/templates/main_page.html new file mode 100644 index 000000000..021c52714 --- /dev/null +++ b/src/data/templates/main_page.html @@ -0,0 +1,26 @@ +{% extends "gramps-base.html" %} + +{% block title %}GRAMPS Connect - main page {% endblock %} +{% block heading %}GRAMPS - main page {% endblock %} + +{% block content %} + +

Welcome to GRAMPS Connect, a new web-based collaboration tool. + +{% if user.is_authenticated %} + You are now logged in + as {{user.username}}. +{% endif %} +

+ +

+Database information: +

+

+ +{% endblock %} + diff --git a/src/data/templates/person_detail-one name.html b/src/data/templates/person_detail-one name.html new file mode 100644 index 000000000..d03eab6c4 --- /dev/null +++ b/src/data/templates/person_detail-one name.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} + +{% block windowtitle %}Person Details{% endblock %} + +{% block pagetitle %}{{Pname}}{% endblock %} + +{% block content %} +
+ + + {% for field in NForm %} +
+ + + + + + {% endfor %} +
Preferred Name
{{ field.label_tag }}: {{ field }}

{{ field.help_text }}

{{ field.errors }}
+ + {% for field in PForm %} +
+ + + + + {% endfor %} +
{{ field.label_tag }}: {{ field }}

{{ field.help_text }}

{{ field.errors }}
+ +
+{% endblock %} \ No newline at end of file diff --git a/src/data/templates/person_detail.html b/src/data/templates/person_detail.html new file mode 100644 index 000000000..fc16e7532 --- /dev/null +++ b/src/data/templates/person_detail.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} + +{% block windowtitle %}Person Details{% endblock %} + +{% block pagetitle %}{{Pname}}{% endblock %} + +{% block content %} +
+ {{ NForm.management_form }} + Names (hide) +
+ + + + + + + + + + + {% for form in NForm.forms %} + + {% for field in form %} + + {% endfor %} + + {% endfor %} + +
Preferred Name?PrefixFirst NameSurnameSuffixType of NameDelete

{{ field }}

+

{{ field.errors }}

{{ NamesetError }}
+ + + + + + + + + + + + + +
Reference ID: {{PForm.gramps_id}}{{PForm.private}} Private Last Changed: {{PLastChanged}} +

{{PForm.last_changed.errors}}

Marker: {{PForm.marker_type}} +

{{PForm.marker_type.errors}}

Gender: {{PForm.gender_type}} +

{{PForm.gender_type.errors}}

Parent in these families: + ( + Edit)

    {% for family in ParentF %} +
  • {{family}}
  • + {% endfor %}

Child in these families: + ( Edit)

    {% for family in ChildF %} +
  • {{family}}
  • + {% endfor %}
+ + + + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/src/data/templates/places.html b/src/data/templates/places.html new file mode 100644 index 000000000..2c6f6202e --- /dev/null +++ b/src/data/templates/places.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block windowtitle %}Places{% endblock %} + +{% block pagetitle %}Places{% endblock %} + +{% block content %} + Congratulations, you found Places +{% endblock %} diff --git a/src/data/templates/pname.html b/src/data/templates/pname.html new file mode 100644 index 000000000..bf4ebd875 --- /dev/null +++ b/src/data/templates/pname.html @@ -0,0 +1,49 @@ + {{ NForm.management_form }} + + + + + + + + + + + + {% for form in NForm.forms %} +
+ + {% for field in form %} + + {% endfor %} + + + + + + + {% endfor %} + +{{ NForm.management_form }} +
Names:
{{ field }}
{{ field.label_tag }}: {{ field }}

{{ field.help_text }}

{{ field.errors }}
+ + + + + + + + + {% for form in NForm.forms %} + {% for field in form %} + + {% endfor %} + + + {% endfor %} +
Preferred Name?PrefixFirst NameSurnameSuffixType of NameDelete
{{ field }}

{{ field.errors }}

\ No newline at end of file diff --git a/src/data/templates/registration/login.html b/src/data/templates/registration/login.html new file mode 100644 index 000000000..4833e680b --- /dev/null +++ b/src/data/templates/registration/login.html @@ -0,0 +1,35 @@ +{% extends "gramps-base.html" %} + +{% block title %}GRAMPS Connect - login {% endblock %} +{% block heading %}GRAMPS - login {% endblock %} + +{% block content %} +

User Login

+{% if form.errors %} +

Your username or password were not valid. Please try again.

+{% else %} +

Enter your login ID and password below.

+{% endif %} +
+ + + + + + + + + +
+ + + {{form.username}} +
+ + + {{form.password}} +
+ + +
+{% endblock %} diff --git a/src/data/templates/successful_data_change.html b/src/data/templates/successful_data_change.html new file mode 100644 index 000000000..ad07ad98b --- /dev/null +++ b/src/data/templates/successful_data_change.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block windowtitle %}Data Changes Successfully Saved{% endblock %} + +{% block pagetitle %}Data Changes Successfully Saved{% endblock %} + +{% block content %} + Congratulations, your data changes were successful! +{% endblock %} diff --git a/src/data/templates/table_header.html b/src/data/templates/table_header.html new file mode 100644 index 000000000..d3894883b --- /dev/null +++ b/src/data/templates/table_header.html @@ -0,0 +1,5 @@ +{% for header in headers %} + {% if header.sortable %}{% endif %} + {{ header.text }} + {% if header.sortable %}{% endif %} +{% endfor %} diff --git a/src/data/templates/user_page.html b/src/data/templates/user_page.html new file mode 100644 index 000000000..e9cd0d830 --- /dev/null +++ b/src/data/templates/user_page.html @@ -0,0 +1,20 @@ +{% extends "gramps-base.html" %} + +{% block title %}GRAMPS Connect - user page {% endblock %} +{% block heading %}GRAMPS - user page {% endblock %} + +{% block content %} + +

Details for {{user.first_name}} {{user.last_name}} ({{user.username}}):

+ +

+

+

+ +{% endblock %} + diff --git a/src/data/templates/view_detail_page.html b/src/data/templates/view_detail_page.html new file mode 100644 index 000000000..c54349e42 --- /dev/null +++ b/src/data/templates/view_detail_page.html @@ -0,0 +1,10 @@ +{% extends "gramps-base.html" %} +{% block title %}GRAMPS Connect - {{cview}} detail page {% endblock %} +{% block heading %}GRAMPS - {{cview}} detail page {% endblock %} + +{% block content %} + +{{cview}} Detail page. + +{% endblock %} + diff --git a/src/data/templates/view_events.html b/src/data/templates/view_events.html new file mode 100644 index 000000000..e10f81e07 --- /dev/null +++ b/src/data/templates/view_events.html @@ -0,0 +1,34 @@ +{% extends "view_page.html" %} +{% load my_tags %} + +{% block table_data %} + + + + + + + + + + + + + {% for family in page.object_list %} + + + + {% endfor %} + +
Item #IDFatherMotherRelationship
{{ forloop.counter|row_count:page }}[{{family.gramps_id}}] + {{family.father.name_set|make_name:user}} + {{family.mother.name_set|make_name:user}} + {% if user.is_authenticated %} + {{family.family_rel_type|escape}} + {% else %} + [Private] + {% endif %} +
+ +{% endblock %} + diff --git a/src/data/templates/view_family.html b/src/data/templates/view_family.html new file mode 100644 index 000000000..e10f81e07 --- /dev/null +++ b/src/data/templates/view_family.html @@ -0,0 +1,34 @@ +{% extends "view_page.html" %} +{% load my_tags %} + +{% block table_data %} + + + + + + + + + + + + + {% for family in page.object_list %} + + + + {% endfor %} + +
Item #IDFatherMotherRelationship
{{ forloop.counter|row_count:page }}[{{family.gramps_id}}] + {{family.father.name_set|make_name:user}} + {{family.mother.name_set|make_name:user}} + {% if user.is_authenticated %} + {{family.family_rel_type|escape}} + {% else %} + [Private] + {% endif %} +
+ +{% endblock %} + diff --git a/src/data/templates/view_navigation.html b/src/data/templates/view_navigation.html new file mode 100644 index 000000000..569a7f5e4 --- /dev/null +++ b/src/data/templates/view_navigation.html @@ -0,0 +1,13 @@ +{% load my_tags %} + + + +{% table_header %} + + + + {% for view in view_list %} + + {% endfor %} + +
{{ view.name|escape }}
diff --git a/src/data/templates/view_page.html b/src/data/templates/view_page.html new file mode 100644 index 000000000..92eb738fe --- /dev/null +++ b/src/data/templates/view_page.html @@ -0,0 +1,81 @@ +{% extends "gramps-base.html" %} +{% load my_tags %} + +{% block title %}GRAMPS Connect - {{cview}} view page {% endblock %} +{% block heading %}GRAMPS - {{cview}} view page {% endblock %} + +{% block content %} + +

+

+ + +
+

+ + + +

 

+ +{% block table_data %} TABLE DATA GOES HERE {% endblock %} +
+ + + +{% endblock %} diff --git a/src/data/templates/view_person.html b/src/data/templates/view_person.html new file mode 100644 index 000000000..bb8f719eb --- /dev/null +++ b/src/data/templates/view_person.html @@ -0,0 +1,41 @@ +{% extends "view_page.html" %} +{% load my_tags %} + +{% block table_data %} + + + + + + + + + + + + + + + + + {% for name in page.object_list %} + + + + + + {% if user.is_authenticated %} + + + {% else %} + + {% endfor %} + +
Item #NameIDGenderBirth DateDeath Date
{{ forloop.counter|row_count:page }}{{name|make_name:user}} + [{{name.person.gramps_id|escape}}]{{name.person.gender_type|escape}}{{name.person|person_get_birth_date}}{{name.person|person_get_death_date}}[Private] + [Private] + {% endif %} +
+ +{% endblock %} + diff --git a/src/data/templates/view_repository.html b/src/data/templates/view_repository.html new file mode 100644 index 000000000..e10f81e07 --- /dev/null +++ b/src/data/templates/view_repository.html @@ -0,0 +1,34 @@ +{% extends "view_page.html" %} +{% load my_tags %} + +{% block table_data %} + + + + + + + + + + + + + {% for family in page.object_list %} + + + + {% endfor %} + +
Item #IDFatherMotherRelationship
{{ forloop.counter|row_count:page }}[{{family.gramps_id}}] + {{family.father.name_set|make_name:user}} + {{family.mother.name_set|make_name:user}} + {% if user.is_authenticated %} + {{family.family_rel_type|escape}} + {% else %} + [Private] + {% endif %} +
+ +{% endblock %} + diff --git a/src/gen/web/Makefile b/src/gen/web/Makefile new file mode 100644 index 000000000..7a6b9767f --- /dev/null +++ b/src/gen/web/Makefile @@ -0,0 +1,39 @@ +# Initialize GRAMPS Django site + +update: grampsdb/fixtures/initial_data.json + python manage.py syncdb + +grampsdb/fixtures/initial_data.json: + python init.py > grampsdb/fixtures/initial_data.json + +init_gramps: + python init_gramps.py # clear primary and secondary tables + +run: + python manage.py runserver + +sql: + python manage.py sqlall > gramps_sql.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,Markup,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,Markup,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,Markup,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,Markup,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 diff --git a/src/gen/web/README b/src/gen/web/README new file mode 100644 index 000000000..29bb3f571 --- /dev/null +++ b/src/gen/web/README @@ -0,0 +1,209 @@ +Setting up everything for gramps webapp grampsweb: + +This example will allow you to create a Django Database, populate it +through a GRAMPS export plugin, and then access/edit the data via the +web. + +There are two ways to run this example: using a SQL engine via a +webserver, or using a local SQL database. With either way, you'll be +able to access/edit the data over the web. The only difference is +whether GRAMPS and the webserver accesses the data locally or through +a server. + +First, the easy way with a local sqlite database (steps 2 and 3 are +the same for both): + +1/packages + +sudo yum install subversion sqlite + +2/DO STEP 2 BELOW + +3/DO STEP 3 BELOW + +4/Django settings + +Go to the branches/geps/gep-013-server/webapp/grampsweb dir and edit +settings.py to reflect your setup: + +$ cd webapp/grampsweb +$ gedit settings.py + +and make sure to set to the correct values: + +---------------------------------------------- +DATABASE_ENGINE = 'sqlite3' +# Set the DATABASE_NAME to a full name and path to database file +# This file will be created, so make sure it is in a readable/writeable place. +DATABASE_NAME = '/home/dblank/gramps/gep-013-server/webapp/grampsweb/gramps.sql' +# See file for examples for these: +TIME_ZONE: your time zone +LANGUAGE_CODE: your code +# Full path: +TEMPLATE_DIRS = ( + "/home/dblank/gramps/gep-013-server/webapp/html/templates", +) +---------------------------------------------- + +5/Make database, and then run a webserver: + +make clean +make +make run + +6/Run the Django Export plugin in this version of GRAMPS: + +(you may need to ./autogen.sh; make in top level of branch) + +python ../../src/gramps.py + +Open a family tree, and select in menu Family Tree the export +entry. Select Django export. (You used to have to give a filename; +that is fixed now.) The django installation is querried and the django +backend as given in the settings is used to write to. + +NOTE: You must manually empty the tables or delete them and regenerate +for now if you reexport your data, as the export plugin expects an +empty database. If the database is not empty, duplication errors will +occur. + +7/You can (after this plugin has finished running) go back to eg +pgadmin or the http://127.0.0.1:8000/admin to see that the objects +have been saved. You can edit them here, too. + +See the following for more details, and for setting up a more +sophisticated database server. + +---------------------------------------------- + +1/packages + +benny@antec:sudo apt-get install python-psycopg python-psycopg2 postgresql apache2 libapache2-mod-python subversion pgadmin3 pgadmin3-data + +2/get the django code.and install it + +See http://docs.djangoproject.com/en/dev/topics/install/ + + +3/get webapp sourcecode + +benny@antec:mkdir branches/geps;cd branches/geps +**** extract data here *** +svn co https://gramps.svn.sourceforge.net/svnroot/gramps/branches/geps/gep-013-server/ gep013-server + +Now go to the gramps code, and set it up + +cd gep013-server +./autogen.sh +make + +4/set up PostgreSQL +Reset the password for the 'postgres' admin account for the server + +benny@antec:cd ~ +benny@antec:sudo su postgres -c psql template1 +template1=# ALTER USER postgres WITH PASSWORD 'password'; +template1=# \q + +Add a new user into postgresql: + +benny@antec:sudo su postgres -c createuser gramps +Enter name of role to add: gramps +Shall the new role be a superuser? (y/n) y + +Now define the access rules for the database. The easiest is to give access to all databases via md5: + +benny@antec:sudo kate /etc/postgresql/8.3/main/pg_hba.conf + +and end of file should look like: +# "local" is for Unix domain socket connections only +local all all md5 +# IPv4 local connections: +host all all 127.0.0.1/32 md5 +# IPv6 local connections: +host all all ::1/128 md5 + +Restart the server: + +benny@antec:~$ sudo /etc/init.d/postgresql-8.3 restart + +5/Use pgAdmin3 to access the database server. + +benny@antec:pgadmin3 + +In the new server registration dialog, give a name for your server, and set: +Host: localhost +Maintenance DB: postgres +Username: postgres +Password: 'password' ==> the password entered above in 4/ + +Do not store the password! Next click to connect (Menu Tools->Connect). + +If all is well, postgresql is set up and you see your server running. The login roles should contain the user ´gramps` you created in 4/ + +First step is to make a database which gramps webapp can use, so right click on your server, and add a database: +name: db_gramps +owner: gramps +encoding: UTF-8 + +Next, go to the role 'gramps' and assign a password with which the user can +access. + +That's it, the tables will be created with django. + + +6/Django settings + +Go to the webapp code and change your settings to reflect your setup. + +benny@antec:cd webapp/grampsweb +benny@antec:kate settings.py + +and make sure to set to the correct values: + +DATABASE_USER: as given in 4/, standard is gramps +DATABASE_PASSWORD: the password for the user in the database grampsonrails will create +TIME_ZONE: your time zone +LANGUAGE_CODE: your code +TEMPLATE_DIRS = ( + "/yourfullpath/gep-013-server/webapp/html/templates", +) +.... + +See the Django site for more info when changing variables: +http://docs.djangoproject.com/en/dev/topics/settings/#topics-settings + +7/run your webserver + +For production, you should use Apache. For testing locally, you can use the django webserver, which is available via the manage.py file in the webapp directory. +Note that we need GRAMPS libraries, so we give the GRAMPS path for inclusion in PYTHONPATH. For effective distribution, we will ship the required libraries of GRAMPS in the application grampsweb. For my case this is + +benny@antec: PYTHONPATH=~/programs/gramps/branches/geps/gep013-server/src/ python manage.py runserver & + +Try it in your webbrowser by going to: + +http://127.0.0.1:8000/admin + +8/Create tables and start the django application + +benny@antec: python manage.py syncdb + +Create the superuser for django at this moment if needed. + +9/To see the SQL to create the tables, do: + +PYTHONPATH=~/programs/gramps/branches/geps/gep013-server/src/ python manage.py sqlall grampsdb + + +10/Now that a beginning of the tables is present, we need to pump data into it. For now, we assume all tables are empty, and we add new data to it. If the tables are not empty, drop them first, eg via pgAdmin for postgresql. +A new export plugin that can write to your django backend is written, ExportDjango. If your database exists, then you can just open a family tree, and select to export this to the django backend. At the moment only markertype and part of the person object are exported as a proof of concept. + +11/You can after this plugin is finished go back to eg pgadmin or the http://127.0.0.1:8000/admin to see the objects have been saved. + +References: +[1] http://ianlawrence.info/random-stuff/set-up-django-apache-and-postgresql-on-ubuntu-feisty/ +[2] http://www.postgresql.org/docs/8.1/static/app-createuser.html +[3] http://antoniocangiano.com/2007/12/26/installing-django-with-postgresql-on-ubuntu/ +[4] http://www.mail-archive.com/django-users@googlegroups.com/msg36675.html + + diff --git a/src/gen/web/__init__.py b/src/gen/web/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/gen/web/djangodb.py b/src/gen/web/djangodb.py new file mode 100644 index 000000000..7083851ef --- /dev/null +++ b/src/gen/web/djangodb.py @@ -0,0 +1,249 @@ +import gen +from gen.db import GrampsDbBase +from gen.web.libdjango import DjangoInterface +import Utils + +# class Trans(object): +# def __init__(self, dji_model): +# self.dji_model = dji_model + +# def get(self, handle): +# return self.dji_model.filter(handle=handle) == 1 + +# def keys(self): +# return [item.handle for item in self.dji_model.all()] + +# from ReportBase._CommandLineReport import run_report_direct +# import djangodb +# db = djangodb.DjangoDb() +# run_report_direct(db, "ancestor_report", off="pdf", of="ar.pdf") +# + +def probably_alive(handle): + db = DjangoDb() + return Utils.probably_alive(db.get_person_from_handle(handle), db) + +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 + +class DjangoDb(GrampsDbBase): + """ + A Gramps Database Backend. This replicates the grampsdb functions. + """ + + def __init__(self): + super(DjangoDb, self).__init__() + self.dji = DjangoInterface() + self.readonly = False + self.db_is_open = True + + def get_event_from_handle(self, handle): + obj = gen.lib.Event() + obj.unserialize(self.dji.get_event(self.dji.Event.get(handle=handle))) + return obj + + def get_family_from_handle(self, handle): + obj = gen.lib.Family() + obj.unserialize(self.dji.get_family(self.dji.Family.get(handle=handle))) + return obj + + def get_person_from_handle(self, handle): + obj = gen.lib.Person() + data = self.dji.get_person(self.dji.Person.get(handle=handle)) + obj.unserialize(data) + return obj + + def get_person_from_gramps_id(self, gramps_id): + obj = gen.lib.Person() + data = self.dji.get_person(self.dji.Person.get(gramps_id=gramps_id)) + obj.unserialize(data) + return obj + + def get_number_of_people(self): + return self.dji.Person.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.dji.get_place) + + def get_person_cursor(self): + return Cursor(self.dji.Person, self.dji.get_person) + + 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): + return self.dji.get_person(self.dji.Person.get(handle=handle)) + + def get_raw_family_data(self, handle): + return self.dji.get_family(self.dji.Family.get(handle=handle)) + + def get_raw_source_data(self, handle): + return self.dji.get_source(self.dji.Source.get(handle=handle)) + + def get_raw_repository_data(self, handle): + return self.dji.get_repository(self.dji.Repository.get(handle=handle)) + + def get_raw_note_data(self, handle): + return self.dji.get_note(self.dji.Note.get(handle=handle)) + + def get_raw_place_data(self, handle): + return self.dji.get_place(self.dji.Place.get(handle=handle)) + + def add_person(self, person, trans, set_gid=True): + pass + # if self.step == 0: + # if not person.handle: + # person.handle = Utils.create_id() + # if not person.gramps_id: + # person.gramps_id = self.find_next_person_gramps_id() + # self.lookup[person.gramps_id] = person.handle + # if self.dji.Person.filter(handle=person.handle).count() == 0: + # print "add_person:", person.handle + # self.dji.add_person(person.serialize()) + # else: + # print "update_person:", person.handle + # person.handle = self.lookup[person.gramps_id] + # self.dji.add_person_detail(person.serialize()) + + def add_family(self, family, trans, set_gid=True): + pass + # if self.step == 0: + # if not family.handle: + # family.handle = Utils.create_id() + # if not family.gramps_id: + # family.gramps_id = self.find_next_family_gramps_id() + # self.lookup[family.gramps_id] = family.handle + # if self.dji.Family.filter(handle=family.handle).count() == 0: + # print "add_family:", family.handle + # self.dji.add_family(family.serialize()) + # else: + # family.handle = self.lookup[family.gramps_id] + # self.dji.add_family_detail(family.serialize()) + + def add_source(self, source, trans, set_gid=True): + pass + #print "add_source:", source.handle + #if not source.handle: + # source.handle = Utils.create_id() + # self.dji.add_source(source.serialize()) + #self.dji.add_source_detail(source.serialize()) + + def add_repository(self, repository, trans, set_gid=True): + pass + #print "add_repository:", repository.handle + #if not repository.handle: + # repository.handle = Utils.create_id() + # self.dji.add_repository(repository.serialize()) + #self.dji.add_repository_detail(repository.serialize()) + + def add_note(self, note, trans, set_gid=True): + pass + #print "add_note:", note.handle + #if not note.handle: + # note.handle = Utils.create_id() + # self.dji.add_note(note.serialize()) + #self.dji.add_note_detail(note.serialize()) + + def add_place(self, place, trans, set_gid=True): + #print "add_place:", place.handle + pass + + def add_event(self, event, trans, set_gid=True): + pass + #print "add_event:", event.handle + #if not event.handle: + # event.handle = Utils.create_id() + # self.dji.add_event(event.serialize()) + #self.dji.add_event_detail(event.serialize()) + + def commit_person(self, person, trans, change_time=None): + pass + #print "commit_person:", person.handle + #self.add_person(person, trans) + + def commit_family(self, family, trans, change_time=None): + pass + #print "commit_family:", family.handle + #self.add_family(family, trans) + + def commit_source(self, source, trans, change_time=None): + pass + #print "commit_source:", source.handle + #self.add_source(source, change_time) + + def commit_repository(self, repository, trans, change_time=None): + pass + #print "commit_repository:", repository.handle + #self.add_repository(repository, change_time) + + def commit_note(self, note, trans, change_time=None): + pass + #print "commit_note:", note.handle + #self.add_note(note, change_time) + + def commit_place(self, place, trans, change_time=None): + pass + #print "commit_place:", place.handle + #if self.dji.Place.filter(handle=place.handle).count() == 0: + # self.dji.add_place(place.serialize()) + #self.dji.add_place_detail(place.serialize()) + + def commit_event(self, event, change_time=None): + pass + #print "commit_event:", event.handle + #self.add_event(event, change_time) + + # def find_family_from_handle(self, handle, trans): + # obj = gen.lib.Family() + # results = self.dji.Family.filter(handle=handle) + # if results.count() == 0: + # obj.handle = handle + # new = True + # else: + # data = self.dji.get_family(results[0]) + # obj.unserialize(data) + # new = False + # return obj, new + + diff --git a/src/gen/web/forms.py b/src/gen/web/forms.py new file mode 100644 index 000000000..eff4b60de --- /dev/null +++ b/src/gen/web/forms.py @@ -0,0 +1,69 @@ +# forms.py forms for Django project gen.web + +from django import forms +from gen.web.grampsdb.models import * +from django.forms.models import inlineformset_factory +from django.forms.models import BaseModelFormSet +import datetime + +class PersonForm(forms.ModelForm): + class Meta: + model = Person + exclude = ('handle',) + '''def clean(self): + cleaned_data['last_changed'] = datetime.datetime.now() + super(PersonForm, self).clean() # validate based on model + return self.cleaned_data''' + +class NameForm(forms.ModelForm): + class Meta: + model = Name + +'''class NameFormset(BaseModelFormSet): + def __init__(self, *args, **kwargs): + self.form = NameForm + super(NameFormset, self).__init__(*args, **kwargs) + +def makeNameFormset(pid): + class NameFormset(BaseFormSet): + def __init__(self, *args, **kwargs): + self.form = NameForm + self.queryset = Name.objects.filter(person=pid) + super(NameFormset, self).__init__(*args, **kwargs) + def clean(self): + super(NameFormset, self).clean() # validate based on model + if any(self.errors): + # formset is not valid as long as any one form is invalid + return + # allow only one name per person to be preferred + ctPref = 0 + for i in range(0, self.total_form_count()): + form = self.forms[i] + ctPref += form.cleaned_data['preferred'] + if ctPref > 1: + raise forms.ValidationError, "Only one name may be the preferred name." + return NameFormset''' + +NameInlineFormSet = inlineformset_factory(Person, Name, + fields=('preferred','prefix','first_name', + 'surname','suffix','name_type'), + form=NameForm) + +def cleanPreferred(fmst): + if fmst.total_form_count() == 3: # person has no names (assumes default 3 extra forms) + return "Error: Each person must have at least one name." + ctPref = 0 + for i in range (0,fmst.total_form_count()-3): + form = fmst.forms[i] + try: # when preferred is false, its value is not in the form data + if form.data[fmst.prefix + '-' + str(i) + '-preferred'] == 'on': + val = 1 + else: + val = 0 + except: + val = 0 + ctPref += val + if ctPref != 1: + return "Error: Exactly one name may be the preferred name." + else: + return "none" diff --git a/src/gen/web/grampsdb/__init__.py b/src/gen/web/grampsdb/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/gen/web/grampsdb/admin.py b/src/gen/web/grampsdb/admin.py new file mode 100644 index 000000000..aa4e3a8b2 --- /dev/null +++ b/src/gen/web/grampsdb/admin.py @@ -0,0 +1,6 @@ +from gen.web.grampsdb.models import * +from django.contrib import admin + +for type_name in get_tables("all"): + admin.site.register(type_name[1]) + diff --git a/src/gen/web/grampsdb/models.py b/src/gen/web/grampsdb/models.py new file mode 100644 index 000000000..80943f6ab --- /dev/null +++ b/src/gen/web/grampsdb/models.py @@ -0,0 +1,873 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2009 B. Malengier +# Copyright (C) 2009 Douglas S. Blank +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 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 + +#--------------------------------------------------------------------------- +# +# 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 MarkerType(mGrampsType): + from gen.lib.markertype import MarkerType + _DATAMAP = get_datamap(MarkerType) + _CUSTOM = MarkerType._CUSTOM + _DEFAULT = _DATAMAP[MarkerType._DEFAULT] + val = models.IntegerField('marker', choices=_DATAMAP, blank=False) + +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 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) + +#--------------------------------------------------------------------------- +# +# Support definitions +# +#--------------------------------------------------------------------------- + +class DateObject(models.Model): + class Meta: abstract = True + + calendar = models.IntegerField() + modifier = models.IntegerField() + quality = models.IntegerField() + day1 = models.IntegerField() + month1 = models.IntegerField() + year1 = models.IntegerField() + slash1 = models.BooleanField() + 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() + newyear = models.IntegerField() + + 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 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 + private = models.BooleanField('private') + #attributes = models.ManyToManyField("Attribute", blank=True, null=True) + + ## Keys: + marker_type = models.ForeignKey('MarkerType') + + 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') + 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") + #lds_list = models.ManyToManyField('Lds', null=True, blank=True) + #url_list = models.ManyToManyField('Url', null=True, blank=True) + + # Others keys here: + # .name_set + # .address_set + # .lds_set + # .url_set + +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') + #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") + +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") + +#--------------------------------------------------------------------------- +# +# 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 + order = models.PositiveIntegerField() + +class Name(DateObject, SecondaryObject): + name_type = models.ForeignKey('NameType', related_name="name_code") + preferred = models.BooleanField('preferred name?') + first_name = models.TextField(blank=True) + surname = models.TextField(blank=True) + suffix = models.TextField(blank=True) + title = models.TextField(blank=True) + prefix = models.TextField(blank=True) + patronymic = models.TextField(blank=True) + call = models.TextField(blank=True) + group_as = models.TextField(blank=True) + sort_as = models.IntegerField(blank=True) + display_as = models.IntegerField(blank=True) + + ## Key: + person = models.ForeignKey("Person") + + def __unicode__(self): + return "%s%s%s, %s" % (self.prefix, + ["", " "][bool(self.prefix)], + self.surname, + self.first_name) + +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) + 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 + #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) + +TABLES = [ + ("abstract", mGrampsType), + ("type", MarkerType), + ("type", MarkupType), + ("type", NameType), + ("type", AttributeType), + ("type", UrlType), + ("type", ChildRefType), + ("type", RepositoryType), + ("type", EventType), + ("type", FamilyRelType), + ("type", SourceMediaType), + ("type", EventRoleType), + ("type", NoteType), + ("type", GenderType), + ("type", LdsType), + ("type", LdsStatus), + ("abstract", DateObject), + ("meta", Config), + ("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", 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) + ] + +def clear_tables(*categories): + """ + Clear the entries of categories of tables. Category is: + "abstract", "type", "ref", "meta", "primary" and "secondary". + """ + for pair in get_tables(*categories): + pair[1].objects.all().delete() + +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"] + +#--------------------------------------------------------------------------- +# +# Testing Functions +# +#--------------------------------------------------------------------------- + +## Primary: + +def test_Person(): + m = get_default_type(MarkerType) + p = Person(handle=create_id(), marker_type=m) + p.gender_type = GenderType.objects.get(id=1) + p.gramps_id = "P%05d" % (Person.objects.count() + 1) + p.save() + return p + +def test_Family(): + m = get_default_type(MarkerType) + frt = FamilyRelType.objects.get(id=1) + f = Family(handle=create_id(), marker_type=m, family_rel_type=frt) + f.gramps_id = "F%05d" % (Family.objects.count() + 1) + f.save() + return f + +def test_Source(): + m = get_default_type(MarkerType) + s = Source(handle=create_id(), marker_type=m) + s.save() + s.gramps_id = "S%05d" % (Source.objects.count() + 1) + s.save() + return s + +def test_Event(): + m = get_default_type(MarkerType) + et = get_default_type(EventType) + e = Event(handle=create_id(), marker_type=m, event_type=et) + e.set_date_from_gdate( GDate() ) + e.gramps_id = "E%05d" % (Event.objects.count() + 1) + e.save() + return e + +def test_Repository(): + m = get_default_type(MarkerType) + rt = get_default_type(RepositoryType) + r = Repository(handle=create_id(), marker_type=m, repository_type=rt) + r.gramps_id = "R%05d" % (Repository.objects.count() + 1) + r.save() + return r + +def test_Place(): + m = get_default_type(MarkerType) + p = Place(handle=create_id(), marker_type=m) + p.gramps_id = "L%05d" % (Place.objects.count() + 1) + p.save() + return p + +def test_Media(): + m = get_default_type(MarkerType) + media = Media(handle=create_id(), marker_type=m) + media.set_date_from_gdate( GDate() ) + media.save() + media.gramps_id = "M%05d" % (Media.objects.count() + 1) + return media + +def test_Note(): + m = get_default_type(MarkerType) + note_type = get_default_type(NoteType) + note = Note(handle=create_id(), marker_type=m, note_type=note_type, + preformatted=False) + note.gramps_id = "N%05d" % (Note.objects.count() + 1) + note.save() + return note + +def test_Family_with_children(): + father = test_Person() + fname = test_Name(father, "Blank", "Lowell") + mother = test_Person() + mname = test_Name(mother, "Bamford", "Norma") + family_rel_type = get_default_type(FamilyRelType) + m = get_default_type(MarkerType) + f = Family(handle=create_id(), father=father, mother=mother, + family_rel_type=family_rel_type, marker_type=m) + f.save() + for names in [("Blank", "Doug"), ("Blank", "Laura"), ("Blank", "David")]: + p = test_Person() + n = test_Name(p, names[0], names[1]) + p.families.add(f) + f.save() + return f + +## Secondary: + +def test_Name(person=None, surname=None, first=None): + if not person: # Testing + person = test_Person() + m = get_default_type(MarkerType) + n = Name() + if first: + n.first_name = first + if surname: + n.surname = surname + n.set_date_from_gdate(Today()) + n.name_type = get_default_type(NameType) + n.order = 1 + n.sort_as = 1 + n.display_as = 1 + person.save() + n.person = person + n.save() + return n + +def test_Markup(note=None): + if not note: + note = test_Note() + markup = Markup(note=note, + markup_type=get_type(MarkupType, + (1, "Testing"), + get_or_create=True)) + markup.order = 1 + markup.save() + return markup + +def test_Lds(place=None, famc=None): + if not place: + place = test_Place() + if not famc: + famc = test_Family() + lds = Lds(lds_type=get_default_type(LdsType), status=get_default_type(LdsStatus), + place=place, famc=famc, order=1) + lds.set_date_from_gdate(Today()) + lds.save() + return lds + +def test_NoteRef(): + note = test_Note() + person = test_Person() + note_ref = NoteRef(referenced_by=person, ref_object=note) + note_ref.order = 1 + note_ref.save() + family = test_Family() + note_ref = NoteRef(referenced_by=family, ref_object=note) + note_ref.order = 1 + note_ref.save() + return note_ref + +def test_SourceRef(): + note = test_Note() + source = test_Source() + source_ref = SourceRef(referenced_by=note, ref_object=source, confidence=4) + source_ref.set_date_from_gdate(Today()) + source_ref.order = 1 + source_ref.save() + return source_ref + +#--------------------------------------------------------------------------- +# +# Testing +# +#--------------------------------------------------------------------------- + +def main(): + for test_Item in [test_Person, test_Family, test_Family_with_children, + test_Source, test_Event, + test_Repository, test_Place, test_Media, test_Note, + test_Name, test_Markup, test_Lds, test_NoteRef, + test_SourceRef]: + print "testing:", test_Item.__name__ + obj = test_Item() + + sourceref = test_SourceRef() + print sourceref.ref_object.references.all() + +if __name__ == "__main__": + main() diff --git a/src/gen/web/grampsdb/templatetags/__init__.py b/src/gen/web/grampsdb/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/gen/web/grampsdb/templatetags/my_tags.py b/src/gen/web/grampsdb/templatetags/my_tags.py new file mode 100644 index 000000000..bfe0aa408 --- /dev/null +++ b/src/gen/web/grampsdb/templatetags/my_tags.py @@ -0,0 +1,148 @@ +from django.template import escape, Library +from gen.web import libdjango +from gen.web import djangodb +import gen.web.grampsdb.models as models +from gen.lib.date import Date as GDate, Today +import DateHandler + +dji = libdjango.DjangoInterface() +register = Library() + +_dd = DateHandler.displayer.display +_dp = DateHandler.parser.parse + +## FIXME: these dji function wrappers just use the functions +## written for the import/export. Can be done much more directly. + +def person_get_birth_date(person): + return person_get_event(person, models.EventType.BIRTH) +def person_get_death_date(person): + return person_get_event(person, models.EventType.DEATH) + +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) + +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 person_get_event(person, event_type): + event_ref_list = dji.get_event_ref_list(person) + 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 "" + +def make_name(name, user): + if isinstance(name, models.Name): + surname = name.surname.strip() + if not surname: + surname = "[Missing]" + if user.is_authenticated(): + return escape("%s, %s" % (surname, name.first_name)) + else: + if djangodb.probably_alive(name.person.handle): + return escape("%s, %s" % (surname, "[Living]")) + else: + return escape("%s, %s" % (surname, name.first_name)) + elif name: + name = name.get(preferred=True) + if name: + return make_name(name, user) + else: + return "" + else: + return "" +make_name.is_safe = True +register.filter('make_name', make_name) + +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 "CurrentSection" + return "OtherSection" +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 view_navigation(context): + # add things for the view here + return context + +register.inclusion_tag('view_navigation.html', + takes_context=True)(view_navigation) + +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. + + """ +## Alternative page_numbers: + page_numbers = range(max(0, context['page']-adjacent_pages), + min(context['pages'], + context['page']+adjacent_pages)+1) + results_this_page = context['object_list'].__len__() + range_base = ((context['page'] - 1) * context['results_per_page']) + +# # Original +# # page_numbers = [n for n in range(context['page'] - adjacent_pages, +# # context['page'] + adjacent_pages + 1) +# # if n > 0 and n <= context['pages']] + + return { + 'hits': context['hits'], + 'results_per_page': context['results_per_page'], + 'results_this_page': results_this_page, + 'first_this_page': range_base + 1, + 'last_this_page': range_base + results_this_page, + 'page': context['page'], + 'pages': context['pages'], + 'page_numbers': page_numbers, + 'next': context['next'], + 'previous': context['previous'], + 'has_next': context['has_next'], + 'has_previous': context['has_previous'], + 'show_first': 1 not in page_numbers, + 'show_last': context['pages'] not in page_numbers, + } + +register.inclusion_tag('paginator.html', + takes_context=True)(paginator) diff --git a/src/gen/web/grampsdb/views.py b/src/gen/web/grampsdb/views.py new file mode 100644 index 000000000..8c362bf09 --- /dev/null +++ b/src/gen/web/grampsdb/views.py @@ -0,0 +1,125 @@ +# Create your views here. + +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 +from django.shortcuts import get_object_or_404, render_to_response +from django.template import Context, RequestContext, escape +from django.db.models import Q + +import gen +from gen.web.grampsdb.models import * +from gen.web.settings import VIEWS + +def get_views(): + ''' + VIEWS is [("People", "person"), (plural, singular), ...] + ''' + return VIEWS + +def main_page(request): + context = RequestContext(request) + context["views"] = [(pair[0], pair[1], + getattr(gen.web.grampsdb.models, pair[2]).objects.count()) + for pair in get_views()] + context["view"] = 'home' + context["cview"] = 'Home' + return render_to_response("main_page.html", context) + +def logout_page(request): + logout(request) + 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["views"] = get_views() + context["view"] = 'user' + context["cview"] = 'User' + return render_to_response('user_page.html', context) + +def view_detail(request, view, handle): + cview = view.title() + context = RequestContext(request) + context["views"] = get_views() + context["cview"] = cview + context["view"] = view + context["handle"] = handle + return render_to_response('view_detail_page.html', context) + +def view(request, view): + cview = view.title() + search = "" + view_template = 'view_page.html' + if view == "event": + object_list = Event.objects.all().order_by("gramps_id") + elif view == "family": + object_list = Family.objects.all().order_by("gramps_id") + view_template = 'view_family.html' + elif view == "media": + object_list = Media.objects.all().order_by("gramps_id") + elif view == "note": + object_list = Note.objects.all().order_by("gramps_id") + elif view == "person": + if request.GET.has_key("search"): + search = request.GET.get("search") + if request.user.is_authenticated(): + if "," in search: + surname, first_name = [term.strip() for term in search.split(",", 1)] + object_list = Name.objects. \ + select_related().filter(surname__icontains=surname, + first_name__icontains=first_name).order_by("surname", "first_name") + else: + object_list = Name.objects. \ + select_related().filter(Q(surname__icontains=search) | + Q(first_name__icontains=search) | + Q(suffix__icontains=search) | + Q(prefix__icontains=search) | + Q(patronymic__icontains=search) | + Q(title__icontains=search) | + Q(person__gramps_id__icontains=search) + ).order_by("surname", "first_name") + else: + # FIXME: non-authenticated users don't get to search first_names + if "," in search: + search, first_name = [term.strip() for term in search.split(",", 1)] + object_list = Name.objects. \ + select_related().filter(surname__icontains=search).order_by("surname", "first_name") + else: + object_list = Name.objects.select_related().order_by("surname", "first_name") + view_template = 'view_person.html' + elif view == "place": + object_list = Place.objects.all().order_by("gramps_id") + elif view == "repository": + object_list = Repository.objects.all().order_by("gramps_id") + elif view == "source": + object_list = Source.objects.all().order_by("gramps_id") + + 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["views"] = get_views() + context["view"] = view + context["cview"] = cview + context["search"] = search + if search: + context["search_query"] = ("&search=%s" % escape(search)) + else: + context["search_query"] = "" + return render_to_response(view_template, context) diff --git a/src/gen/web/init.py b/src/gen/web/init.py new file mode 100644 index 000000000..b40dbd734 --- /dev/null +++ b/src/gen/web/init.py @@ -0,0 +1,92 @@ +""" +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.markertype import MarkerType +from gen.lib.nametype import NameType +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 + +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.0\"")), + (("setting", "\"db_created\""), + ("description", "\"database creation date/time\""), + ("value_type", "\"str\""), + ("value", ('"%s"' % time.strftime("%Y-%m-%d %H:%M")))), + ])]: + 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 = [MarkerType, NameType, AttributeType, UrlType, ChildRefType, + RepositoryType, EventType, FamilyRelType, SourceMediaType, + EventRoleType, NoteType, GenderType, LdsType, LdsStatus] +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 "]" diff --git a/src/gen/web/init_gramps.py b/src/gen/web/init_gramps.py new file mode 100644 index 000000000..d98fa6936 --- /dev/null +++ b/src/gen/web/init_gramps.py @@ -0,0 +1,11 @@ +""" +Clears gramps data +""" + +import os +os.environ["DJANGO_SETTINGS_MODULE"] = "settings" +import settings + +import grampsdb.models as dj + +dj.clear_tables("primary", "secondary") diff --git a/src/gen/web/libdjango.py b/src/gen/web/libdjango.py new file mode 100644 index 000000000..6cd2ad0ff --- /dev/null +++ b/src/gen/web/libdjango.py @@ -0,0 +1,1230 @@ +import time + +import gen.web.grampsdb.models as models +from django.contrib.contenttypes.models import ContentType +import gen + +# To get a django person from a django database: +# djperson = dji.Person.get(handle='djhgsdh324hjg234hj24') +# +# To turn the djperson into a Gramps Person: +# tuple = dji.get_person(djperson) +# gperson = lib.gen.Person(tuple) +# OR +# gperson = djangodb.DjangoDb().get_person_from_handle(handle) + +#------------------------------------------------------------------------- +# +# Import functions +# +#------------------------------------------------------------------------- +def lookup_role_index(role0, event_ref_list): + """ + Find the handle in a unserialized event_ref_list and return code. + """ + if role0 is None: + return -1 + else: + count = 0 + for event_ref in event_ref_list: + (private, note_list, attribute_list, ref, erole) = event_ref + event = models.Event.objects.get(handle=ref) + if event.event_type[0] == role0: + return count + count += 1 + return -1 + +def totime(dtime): + return int(time.mktime(dtime.timetuple())) + +#------------------------------------------------------------------------- +# +# Export functions +# +#------------------------------------------------------------------------- +def todate(t): + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t)) + +def lookup(index, event_ref_list): + """ + Get the unserialized event_ref in an list of them and return it. + """ + if index < 0: + return None + else: + count = 0 + for event_ref in event_ref_list: + (private, note_list, attribute_list, ref, role) = event_ref + if index == count: + return ref + count += 1 + return None + +def get_datamap(grampsclass): + return [x[0] for x in grampsclass._DATAMAP if x[0] != grampsclass.CUSTOM] + +#------------------------------------------------------------------------- +# +# Django Interface +# +#------------------------------------------------------------------------- +class DjangoInterface(object): + """ + DjangoInterface for interoperating between Gramps and Django. + + This interface comes in a number of parts: + get_ITEMS() + add_ITEMS() + + get_ITEM(ITEM) + + Given an ITEM from a Django table, construct a Gramps Raw Data tuple. + + add_ITEM(data) + + Given a Gramps Raw Data tuple, add the data to the Django tables. + + + """ + def __init__(self): + self.debug = 0 + + def __getattr__(self, name): + """ + Django Objects database interface. + + >>> self.Person.all() + >>> self.Person.get(id=1) + >>> self.Person.get(handle='gh71234dhf3746347734') + """ + if hasattr(models, name): + return getattr(models, name).objects + else: + raise AttributeError("no such model: '%s'" % name) + + def get_model(self, name): + if hasattr(models, name): + return getattr(models, name) + else: + raise AttributeError("no such model: '%s'" % name) + + # ----------------------------------------------- + # Get methods to retrieve list data from the tables + # ----------------------------------------------- + + def clear_tables(self, *args): + return models.clear_tables(*args) + + def get_attribute_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + attribute_list = models.Attribute.objects.filter(object_id=obj.id, + object_type=obj_type) + return [self.pack_attribute(attribute) for attribute + in attribute_list] + + def get_names(self, person, preferred): + names = person.name_set.filter(preferred=preferred).order_by("order") + if preferred: + if len(names) > 0: + return self.pack_name(names[0]) + else: + return gen.lib.Name().serialize() + else: + return [self.pack_name(name) for name in names] + + def get_datamap(self, obj): # obj is source + datamap_dict = {} + datamap_list = obj.datamap_set.all() + for datamap in datamap_list: + datamap_dict[datamap.key] = datamap.value + return datamap_dict + + def get_media_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + mediarefs = models.MediaRef.objects.filter(object_id=obj.id, + object_type=obj_type) + retval = [] + for mediaref in mediarefs: + retval.append(self.pack_media_ref(mediaref)) + return retval + + def get_note_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + noterefs = models.NoteRef.objects.filter(object_id=obj.id, + object_type=obj_type) + retval = [] + for noteref in noterefs: + retval.append( noteref.ref_object.handle) + return retval + + def get_repository_ref_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + reporefs = models.RepositoryRef.objects.filter(object_id=obj.id, + object_type=obj_type) + return [self.pack_repository_ref(repo) for repo in reporefs] + + def get_url_list(self, obj): + return [self.pack_url(url) for url in obj.url_set.all().order_by("order")] + + def get_address_list(self, obj, with_parish): # person or repository + addresses = obj.address_set.all().order_by("order") + retval = [] + count = 1 + for address in addresses: + retval.append(self.pack_address(address, with_parish)) + count += 1 + return retval + + def get_child_ref_list(self, family): + obj_type = ContentType.objects.get_for_model(family) + childrefs = models.ChildRef.objects.filter(object_id=family.id, \ + object_type=obj_type).order_by("order") + retval = [] + for childref in childrefs: + retval.append(self.pack_child_ref(childref)) + return retval + + def get_source_ref_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + sourcerefs = models.SourceRef.objects.filter(object_id=obj.id, \ + object_type=obj_type).order_by("order") + retval = [] + for sourceref in sourcerefs: + retval.append(self.pack_source_ref(sourceref)) + return retval + + def get_event_refs(self, obj, order="order"): + obj_type = ContentType.objects.get_for_model(obj) + eventrefs = models.EventRef.objects.filter(object_id=obj.id, \ + object_type=obj_type).order_by(order) + return eventrefs + + def get_event_ref_list(self, obj): + obj_type = ContentType.objects.get_for_model(obj) + eventrefs = models.EventRef.objects.filter(object_id=obj.id, \ + object_type=obj_type).order_by("order") + retval = [] + for eventref in eventrefs: + retval.append(self.pack_event_ref(eventref)) + return retval + + def get_family_list(self, person): # person has families + return [fam.handle for fam in person.families.all()] + + def get_parent_family_list(self, person): + return [fam.handle for fam in person.parent_families.all()] + + def get_person_ref_list(self, person): + obj_type = ContentType.objects.get_for_model(person) + return [self.pack_person_ref(x) for x in + models.PersonRef.objects.filter(object_id=person.id, + object_type=obj_type)] + + def get_lds_list(self, obj): # person or family + return [self.pack_lds(lds) for lds in obj.lds_set.all().order_by("order")] + + def get_place_handle(self, obj): # obj is event + if obj.place: + return obj.place.handle + return '' + + ## Packers: + + def get_event(self, event): + handle = event.handle + gid = event.gramps_id + the_type = tuple(event.event_type) + description = event.description + change = totime(event.last_changed) + marker = tuple(event.marker_type) + private = event.private + note_list = self.get_note_list(event) + source_list = self.get_source_ref_list(event) + media_list = self.get_media_list(event) + attribute_list = self.get_attribute_list(event) + date = self.get_date(event) + place = self.get_place_handle(event) + return (str(handle), gid, the_type, date, description, place, + source_list, note_list, media_list, attribute_list, + change, marker, private) + + def get_note(self, note): + styled_text = [note.text, []] + markups = models.Markup.objects.filter(note=note).order_by("order") + for markup in markups: + value = markup.string + start_stop_list = markup.start_stop_list + ss_list = eval(start_stop_list) + styled_text[1] += [(tuple(markup.markup_type), + value, ss_list)] + changed = totime(note.last_changed) + return (str(note.handle), + note.gramps_id, + styled_text, + note.preformatted, + tuple(note.note_type), + changed, + tuple(note.marker_type), + note.private) + + def get_family(self, family): + child_ref_list = self.get_child_ref_list(family) + event_ref_list = self.get_event_ref_list(family) + media_list = self.get_media_list(family) + attribute_list = self.get_attribute_list(family) + lds_seal_list = self.get_lds_list(family) + source_list = self.get_source_ref_list(family) + note_list = self.get_note_list(family) + if family.father: + father_handle = family.father.handle + else: + father_handle = '' + if family.mother: + mother_handle = family.mother.handle + else: + mother_handle = '' + return (str(family.handle), family.gramps_id, + father_handle, mother_handle, + child_ref_list, tuple(family.family_rel_type), + event_ref_list, media_list, + attribute_list, lds_seal_list, + source_list, note_list, + totime(family.last_changed), + tuple(family.marker_type), + family.private) + + def get_repository(self, repository): + note_list = self.get_note_list(repository) + address_list = self.get_address_list(repository, with_parish=False) + url_list = self.get_url_list(repository) + return (str(repository.handle), + repository.gramps_id, + tuple(repository.repository_type), + repository.name, + note_list, + address_list, + url_list, + totime(repository.last_changed), + tuple(repository.marker_type), + repository.private) + + def get_source(self, source): + note_list = self.get_note_list(source) + media_list = self.get_media_list(source) + datamap = self.get_datamap(source) + reporef_list = self.get_repository_ref_list(source) + return (str(source.handle), + source.gramps_id, + source.title, + source.author, + source.pubinfo, + note_list, + media_list, + source.abbrev, + totime(source.last_changed), + datamap, + reporef_list, + tuple(source.marker_type), + source.private) + + def get_media(self, media): + attribute_list = self.get_attribute_list(media) + source_list = self.get_source_ref_list(media) + note_list = self.get_note_list(media) + date = self.get_date(media) + return (str(media.handle), + media.gramps_id, + media.path, + media.mime, + media.desc, + attribute_list, + source_list, + note_list, + totime(media.last_changed), + date, + tuple(media.marker_type), + media.private) + + def get_person(self, person): + primary_name = self.get_names(person, True) # one + alternate_names = self.get_names(person, False) # list + event_ref_list = self.get_event_ref_list(person) + family_list = self.get_family_list(person) + parent_family_list = self.get_parent_family_list(person) + media_list = self.get_media_list(person) + address_list = self.get_address_list(person, with_parish=False) + attribute_list = self.get_attribute_list(person) + url_list = self.get_url_list(person) + lds_ord_list = self.get_lds_list(person) + psource_list = self.get_source_ref_list(person) + pnote_list = self.get_note_list(person) + person_ref_list = self.get_person_ref_list(person) + # This looks up the events for the first EventType given: + death_ref_index = lookup_role_index(models.EventType.DEATH, event_ref_list) + birth_ref_index = lookup_role_index(models.EventType.BIRTH, event_ref_list) + return (str(person.handle), + person.gramps_id, + tuple(person.gender_type)[0], + primary_name, + alternate_names, + death_ref_index, + birth_ref_index, + event_ref_list, + family_list, + parent_family_list, + media_list, + address_list, + attribute_list, + url_list, + lds_ord_list, + psource_list, + pnote_list, + totime(person.last_changed), + tuple(person.marker_type), + person.private, + person_ref_list) + + def get_date(self, obj): + if ((obj.calendar == obj.modifier == obj.quality == obj.sortval == obj.newyear == 0) and + obj.text == "" and (not obj.slash1) and (not obj.slash2) and + (obj.day1 == obj.month1 == obj.year1 == 0) and + (obj.day2 == obj.month2 == obj.year2 == 0)): + return None + elif ((not obj.slash1) and (not obj.slash2) and + (obj.day2 == obj.month2 == obj.year2 == 0)): + dateval = (obj.day1, obj.month1, obj.year1, obj.slash1) + else: + dateval = (obj.day1, obj.month1, obj.year1, obj.slash1, + obj.day2, obj.month2, obj.year2, obj.slash2) + return (obj.calendar, obj.modifier, obj.quality, dateval, + obj.text, obj.sortval, obj.newyear) + + def get_place(self, place): + locations = place.location_set.all().order_by("order") + main_loc = None + alt_location_list = [] + for location in locations: + if main_loc is None: + main_loc = self.pack_location(location, True) + else: + alt_location_list.append(self.pack_location(location, True)) + url_list = self.get_url_list(place) + media_list = self.get_media_list(place) + source_list = self.get_source_ref_list(place) + note_list = self.get_note_list(place) + return (str(place.handle), + place.gramps_id, + place.title, + place.long, + place.lat, + main_loc, + alt_location_list, + url_list, + media_list, + source_list, + note_list, + totime(place.last_changed), + tuple(place.marker_type), + place.private) + + # --------------------------------- + # Packers + # --------------------------------- + + ## The packers build GRAMPS raw unserialized data. + + ## Reference packers + + def pack_child_ref(self, child_ref): + source_list = self.get_source_ref_list(child_ref) + note_list = self.get_note_list(child_ref) + return (child_ref.private, source_list, note_list, child_ref.ref_object.handle, + tuple(child_ref.father_rel_type), tuple(child_ref.mother_rel_type)) + + def pack_person_ref(self, personref): + source_list = self.get_source_ref_list(personref) + note_list = self.get_note_list(personref) + return (personref.private, + source_list, + note_list, + personref.ref_object.handle, + personref.description) + + def pack_media_ref(self, media_ref): + source_list = self.get_source_ref_list(media_ref) + note_list = self.get_note_list(media_ref) + attribute_list = self.get_attribute_list(media_ref) + if ((media_ref.x1 == media_ref.y1 == media_ref.x2 == media_ref.y2 == -1) or + (media_ref.x1 == media_ref.y1 == media_ref.x2 == media_ref.y2 == 0)): + role = None + else: + role = (media_ref.x1, media_ref.y1, media_ref.x2, media_ref.y2) + return (media_ref.private, source_list, note_list, attribute_list, + media_ref.ref_object.handle, role) + + def pack_repository_ref(self, repo_ref): + note_list = self.get_note_list(repo_ref) + return (note_list, + repo_ref.ref_object.handle, + repo_ref.call_number, + tuple(repo_ref.source_media_type), + repo_ref.private) + + def pack_media_ref(self, media_ref): + note_list = self.get_note_list(media_ref) + attribute_list = self.get_attribute_list(media_ref) + source_list = self.get_source_ref_list(media_ref) + return (media_ref.private, source_list, note_list, attribute_list, + media_ref.ref_object.handle, (media_ref.x1, + media_ref.y1, + media_ref.x2, + media_ref.y2)) + + def pack_event_ref(self, event_ref): + note_list = self.get_note_list(event_ref) + attribute_list = self.get_attribute_list(event_ref) + return (event_ref.private, note_list, attribute_list, + event_ref.ref_object.handle, tuple(event_ref.role_type)) + + def pack_source_ref(self, source_ref): + ref = source_ref.ref_object.handle + confidence = source_ref.confidence + page = source_ref.page + private = source_ref.private + date = self.get_date(source_ref) + note_list = self.get_note_list(source_ref) + return (date, private, note_list, confidence, ref, page) + + def pack_address(self, address, with_parish): + source_list = self.get_source_ref_list(address) + date = self.get_date(address) + note_list = self.get_note_list(address) + locations = address.location_set.all().order_by("order") + if len(locations) > 0: + location = self.pack_location(locations[0], with_parish) + else: + if with_parish: + location = (("", "", "", "", "", "", ""), "") + else: + location = ("", "", "", "", "", "", "") + return (address.private, source_list, note_list, date, location) + + def pack_lds(self, lds): + source_list = self.get_source_ref_list(lds) + note_list = self.get_note_list(lds) + date = self.get_date(lds) + if lds.famc: + famc = lds.famc.handle + else: + famc = None + place = self.get_place_handle(lds) + return (source_list, note_list, date, lds.lds_type[0], place, + famc, lds.temple, lds.status[0], lds.private) + + def pack_source(self, source): + note_list = self.get_note_list(source) + media_list = self.get_media_list(source) + reporef_list = self.get_repository_ref_list(source) + datamap = self.get_datamap(source) + return (source.handle, source.gramps_id, source.title, + source.author, source.pubinfo, + note_list, + media_list, + source.abbrev, + totime(last_changed), datamap, + reporef_list, + tuple(source.marker_type), source.private) + + def pack_name(self, name): + source_list = self.get_source_ref_list(name) + note_list = self.get_note_list(name) + date = self.get_date(name) + return (name.private, source_list, note_list, date, + name.first_name, name.surname, name.suffix, name.title, + tuple(name.name_type), name.prefix, name.patronymic, + name.group_as, name.sort_as, name.display_as, name.call) + + def pack_location(self, loc, with_parish): + if with_parish: + return ((loc.street, loc.city, loc.county, loc.state, loc.country, + loc.postal, loc.phone), loc.parish) + else: + return (loc.street, loc.city, loc.county, loc.state, loc.country, + loc.postal, loc.phone) + + def pack_url(self, url): + return (url.private, url.path, url.desc, tuple(url.url_type)) + + def pack_attribute(self, attribute): + source_list = self.get_source_ref_list(attribute) + note_list = self.get_note_list(attribute) + return (attribute.private, + source_list, + note_list, + tuple(attribute.attribute_type), + attribute.value) + + + ## Export lists: + + def add_child_ref_list(self, obj, ref_list): + ## Currently, only Family references children + for child_data in ref_list: + self.add_child_ref(obj, child_data) + + def add_source_ref_list(self, obj, source_list): + for source_data in source_list: + self.add_source_ref(obj, source_data) + + def add_event_ref_list(self, obj, event_ref_list): + for event_ref in event_ref_list: + self.add_event_ref(obj, event_ref) + + def add_note_list(self, obj, note_list): + for handle in note_list: + # Just the handle + note = models.Note.objects.get(handle=handle) + self.add_note_ref(obj, note) + + def add_alternate_name_list(self, person, alternate_names): + for name in alternate_names: + if name: + self.add_name(person, name, False) + + def add_parent_family_list(self, person, parent_family_list): + for parent_family_data in parent_family_list: + self.add_parent_family(person, parent_family_data) + + def add_media_ref_list(self, person, media_list): + for media_data in media_list: + self.add_media_ref(person, media_data) + + def add_attribute_list(self, obj, attribute_list): + for attribute_data in attribute_list: + self.add_attribute(obj, attribute_data) + + def add_url_list(self, field, obj, url_list): + if not url_list: return None + count = 1 + for url_data in url_list: + self.add_url(field, obj, url_data, count) + count += 1 + + def add_person_ref_list(self, obj, person_ref_list): + for person_ref_data in person_ref_list: + self.add_person_ref(obj, person_ref_data) + + def add_address_list(self, field, obj, address_list): + count = 1 + for address_data in address_list: + self.add_address(field, obj, address_data, count) + count += 1 + + def add_lds_list(self, field, obj, lds_ord_list): + count = 1 + for ldsord in lds_ord_list: + lds = self.add_lds(field, obj, ldsord, count) + #obj.lds_list.add(lds) + #obj.save() + count += 1 + + def add_repository_ref_list(self, obj, reporef_list): + for data in reporef_list: + self.add_repository_ref(obj, data) + + def add_family_ref_list(self, person, family_list): + for family_handle in family_list: + self.add_family_ref(person, family_handle) + + ## Export reference objects: + + def add_person_ref(self, obj, person_ref_data): + (private, + source_list, + note_list, + handle, + desc) = person_ref_data + person = models.Person.objects.get(handle=handle) + count = person.references.count() + person_ref = models.PersonRef(referenced_by=obj, + ref_object=person, + private=private, + order=count + 1, + description=desc) + person_ref.save() + self.add_note_list(person_ref, note_list) + self.add_source_ref_list(person_ref, source_list) + + def add_note_ref(self, obj, note): + count = note.references.count() + note_ref = models.NoteRef(referenced_by=obj, + ref_object=note, + private=False, + order=count + 1) + note_ref.save() + + def add_media_ref(self, obj, media_ref_data): + (private, source_list, note_list, attribute_list, + ref, role) = media_ref_data + media = models.Media.objects.get(handle=ref) + count = media.references.count() + if not role: + role = (0,0,0,0) + media_ref = models.MediaRef(referenced_by=obj, + ref_object=media, + x1=role[0], + y1=role[1], + x2=role[2], + y2=role[3], + private=private, + order=count + 1) + media_ref.save() + self.add_note_list(media_ref, note_list) + self.add_attribute_list(media_ref, attribute_list) + self.add_source_ref_list(media_ref, source_list) + + def add_source_ref(self, obj, source_data): + (date, private, note_list, confidence, ref, page) = source_data + source = models.Source.objects.get(handle=ref) + count = source.references.count() + source_ref = models.SourceRef(private=private, + confidence=confidence, + page=page, + order=count + 1, + referenced_by=obj, + ref_object=source) + self.add_date(source_ref, date) + source_ref.save() + self.add_note_list(source_ref, note_list) + + def add_child_ref(self, obj, data): + (private, source_list, note_list, ref, frel, mrel) = data + child = models.Person.objects.get(handle=ref) + count = models.ChildRef.objects.filter(object_id=obj.id,object_type=obj).count() + child_ref = models.ChildRef(private=private, + referenced_by=obj, + ref_object=child, + order=count + 1, + father_rel_type=models.get_type(models.ChildRefType, frel), + mother_rel_type=models.get_type(models.ChildRefType, mrel)) + child_ref.save() + self.add_source_ref_list(child_ref, source_list) + self.add_note_list(child_ref, note_list) + + def add_event_ref(self, obj, event_data): + (private, note_list, attribute_list, ref, role) = event_data + event = models.Event.objects.get(handle=ref) + count = models.EventRef.objects.filter(object_id=obj.id,object_type=obj).count() + event_ref = models.EventRef(private=private, + referenced_by=obj, + ref_object=event, + order=count + 1, + role_type = models.get_type(models.EventRoleType, role)) + event_ref.save() + self.add_note_list(event_ref, note_list) + self.add_attribute_list(event_ref, attribute_list) + + def add_repository_ref(self, obj, reporef_data): + (note_list, + ref, + call_number, + source_media_type, + private) = reporef_data + repository = models.Repository.objects.get(handle=ref) + count = models.RepositoryRef.objects.filter(object_id=obj.id,object_type=obj).count() + repos_ref = models.RepositoryRef(private=private, + referenced_by=obj, + call_number=call_number, + source_media_type=models.get_type(models.SourceMediaType, + source_media_type), + ref_object=repository, + order=count + 1) + repos_ref.save() + self.add_note_list(repos_ref, note_list) + + def add_family_ref(self, obj, handle): + family = models.Family.objects.get(handle=handle) + obj.families.add(family) + obj.save() + + ## Export individual objects: + + def add_datamap_dict(self, source, datamap_dict): + for key in datamap_dict: + value = datamap_dict[key] + datamap = models.Datamap(key=key, value=value) + datamap.source = source + datamap.save() + #source.datamaps.add(datamap) + #source.save() + + def add_lds(self, field, obj, data, order): + (lsource_list, lnote_list, date, type, place_handle, + famc_handle, temple, status, private) = data + if place_handle: + place = models.Place.objects.get(handle=place_handle) + else: + place = None + if famc_handle: + famc = models.Family.objects.get(handle=famc_handle) + else: + famc = None + lds = models.Lds(lds_type = models.get_type(models.LdsType, type), + temple=temple, + place=place, + famc=famc, + order=order, + status = models.get_type(models.LdsStatus, status), + private=private) + self.add_date(lds, date) + lds.save() + self.add_note_list(lds, lnote_list) + self.add_source_ref_list(lds, lsource_list) + if field == "person": + lds.person = obj + elif field == "family": + lds.family = obj + else: + raise AttributeError("invalid field '%s' to attach lds" % + field) + lds.save() + return lds + + def add_address(self, field, obj, address_data, order): + (private, asource_list, anote_list, date, location) = address_data + address = models.Address(private=private, order=order) + self.add_date(address, date) + address.save() + self.add_location("address", address, location, 1) + self.add_note_list(address, anote_list) + self.add_source_ref_list(address, asource_list) + if field == "person": + address.person = obj + elif field == "repository": + address.repository = obj + else: + raise AttributeError("invalid field '%s' to attach address" % + field) + address.save() + #obj.save() + #obj.addresses.add(address) + #obj.save() + + def add_attribute(self, obj, attribute_data): + (private, source_list, note_list, the_type, value) = attribute_data + attribute_type = models.get_type(models.AttributeType, the_type) + attribute = models.Attribute(private=private, + attribute_of=obj, + attribute_type=attribute_type, + value=value) + attribute.save() + self.add_source_ref_list(attribute, source_list) + self.add_note_list(attribute, note_list) + #obj.attributes.add(attribute) + #obj.save() + + def add_url(self, field, obj, url_data, order): + (private, path, desc, type) = url_data + url = models.Url(private=private, + path=path, + desc=desc, + order=order, + url_type=models.get_type(models.UrlType, type)) + if field == "person": + url.person = obj + elif field == "repository": + url.repository = obj + elif field == "place": + url.place = obj + else: + raise AttributeError("invalid field '%s' to attach to url" % + field) + url.save() + #obj.url_list.add(url) + #obj.save() + + def add_place_ref(self, event, place_handle): + if place_handle: + place = models.Place.objects.get(handle=place_handle) + event.place = place + event.save() + + def add_parent_family(self, person, parent_family_handle): + # handle + family = models.Family.objects.get(handle=parent_family_handle) + person.parent_families.add(family) + person.save() + + def add_date(self, obj, date): + if date is None: + (calendar, modifier, quality, text, sortval, newyear) = \ + (0, 0, 0, "", 0, 0) + day1, month1, year1, slash1 = 0, 0, 0, 0 + day2, month2, year2, slash2 = 0, 0, 0, 0 + else: + (calendar, modifier, quality, dateval, text, sortval, newyear) = date + if len(dateval) == 4: + day1, month1, year1, slash1 = dateval + day2, month2, year2, slash2 = 0, 0, 0, 0 + elif len(dateval) == 8: + day1, month1, year1, slash1, day2, month2, year2, slash2 = dateval + else: + raise AttributeError("ERROR: dateval format '%s'" % dateval) + obj.calendar = calendar + obj.modifier = modifier + obj.quality = quality + obj.text = text + obj.sortval = sortval + obj.newyear = newyear + obj.day1 = day1 + obj.month1 = month1 + obj.year1 = year1 + obj.slash1 = slash1 + obj.day2 = day2 + obj.month2 = month2 + obj.year2 = year2 + obj.slash2 = slash2 + + def add_name(self, person, data, preferred): + if data: + (private, source_list, note_list, date, + first_name, surname, suffix, title, + name_type, prefix, patronymic, + group_as, sort_as, display_as, call) = data + + count = person.name_set.count() + name = models.Name() + name.order = count + 1 + name.preferred = preferred + name.private = private + name.first_name = first_name + name.surname = surname + name.suffix = suffix + name.title = title + name.name_type = models.get_type(models.NameType, name_type) + name.prefix = prefix + name.patronymic = patronymic + name.group_as = group_as + name.sort_as = sort_as + name.display_as = display_as + name.call = call + # we know person exists + # needs to have an ID for key + name.person = person + self.add_date(name, date) + name.save() + self.add_note_list(name, note_list) + self.add_source_ref_list(name, source_list) + #person.save() + + ## Export primary objects: + + def add_person(self, data): + # Unpack from the BSDDB: + (handle, # 0 + gid, # 1 + gender, # 2 + primary_name, # 3 + alternate_names, # 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + url_list, # 13 + lds_ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + private, # 19 + person_ref_list, # 20 + ) = data + + person = models.Person(handle=handle, + gramps_id=gid, + last_changed=todate(change), + private=private, + marker_type = models.get_type(models.MarkerType, marker), + gender_type = models.get_type(models.GenderType, gender)) + person.save() + + def add_person_detail(self, data): + # Unpack from the BSDDB: + (handle, # 0 + gid, # 1 + gender, # 2 + primary_name, # 3 + alternate_names, # 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + url_list, # 13 + lds_ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + private, # 19 + person_ref_list, # 20 + ) = data + + person = models.Person.objects.get(handle=handle) + if primary_name: + self.add_name(person, primary_name, True) + self.add_alternate_name_list(person, alternate_names) + self.add_event_ref_list(person, event_ref_list) + self.add_family_ref_list(person, family_list) + self.add_parent_family_list(person, parent_family_list) + self.add_media_ref_list(person, media_list) + self.add_note_list(person, pnote_list) + self.add_attribute_list(person, attribute_list) + self.add_url_list("person", person, url_list) + self.add_person_ref_list(person, person_ref_list) + self.add_source_ref_list(person, psource_list) + self.add_address_list("person", person, address_list) + self.add_lds_list("person", person, lds_ord_list) + + def add_note_detail(self, data): + """ + Dummy method for consistency with other two-pass adds. + """ + pass + + def add_note(self, data): + # Unpack from the BSDDB: + (handle, gid, styled_text, format, note_type, + change, marker, private) = data + text, markup_list = styled_text + n = models.Note(handle=handle, + gramps_id=gid, + last_changed=todate(change), + private=private, + preformatted=format, + text=text, + marker_type = models.get_type(models.MarkerType, marker), + note_type = models.get_type(models.NoteType, note_type)) + n.save() + count = 1 + for markup in markup_list: + markup_code, value, start_stop_list = markup + m = models.Markup(note=n, order=count, + markup_type=models.get_type(models.MarkupType, markup_code, get_or_create=True), + string=value, + start_stop_list=str(start_stop_list)) + m.save() + + def add_family(self, data): + # Unpack from the BSDDB: + (handle, gid, father_handle, mother_handle, + child_ref_list, the_type, event_ref_list, media_list, + attribute_list, lds_seal_list, source_list, note_list, + change, marker, private) = data + + family = models.Family(handle=handle, gramps_id=gid, + family_rel_type = models.get_type(models.FamilyRelType, the_type), + last_changed=todate(change), + marker_type = models.get_type(models.MarkerType, marker), + private=private) + family.save() + + def add_family_detail(self, data): + # Unpack from the BSDDB: + (handle, gid, father_handle, mother_handle, + child_ref_list, the_type, event_ref_list, media_list, + attribute_list, lds_seal_list, source_list, note_list, + change, marker, private) = data + + family = models.Family.objects.get(handle=handle) + # father_handle and/or mother_handle can be None + if father_handle: + family.father = models.Person.objects.get(handle=father_handle) + if mother_handle: + family.mother = models.Person.objects.get(handle=mother_handle) + family.save() + self.add_child_ref_list(family, child_ref_list) + self.add_note_list(family, note_list) + self.add_attribute_list(family, attribute_list) + self.add_source_ref_list(family, source_list) + self.add_media_ref_list(family, media_list) + self.add_event_ref_list(family, event_ref_list) + self.add_lds_list("family", family, lds_seal_list) + + def add_source(self, data): + (handle, gid, title, + author, pubinfo, + note_list, + media_list, + abbrev, + change, datamap, + reporef_list, + marker, private) = data + source = models.Source(handle=handle, gramps_id=gid, title=title, + author=author, pubinfo=pubinfo, abbrev=abbrev, + last_changed=todate(change), private=private) + source.marker_type = models.get_type(models.MarkerType, marker) + source.save() + + def add_source_detail(self, data): + (handle, gid, title, + author, pubinfo, + note_list, + media_list, + abbrev, + change, datamap, + reporef_list, + marker, private) = data + source = models.Source.objects.get(handle=handle) + self.add_note_list(source, note_list) + self.add_media_ref_list(source, media_list) + self.add_datamap_dict(source, datamap) + self.add_repository_ref_list(source, reporef_list) + + def add_repository(self, data): + (handle, gid, the_type, name, note_list, + address_list, url_list, change, marker, private) = data + + repository = models.Repository(handle=handle, + gramps_id=gid, + marker_type=models.get_type(models.MarkerType, marker), + last_changed=todate(change), + private=private, + repository_type=models.get_type(models.RepositoryType, the_type), + name=name) + repository.save() + + def add_repository_detail(self, data): + (handle, gid, the_type, name, note_list, + address_list, url_list, change, marker, private) = data + + repository = models.Repository.objects.get(handle=handle) + self.add_note_list(repository, note_list) + self.add_url_list("repository", repository, url_list) + self.add_address_list("repository", repository, address_list) + + def add_location(self, field, obj, location_data, order): + if location_data == None: return + if len(location_data) == 7: + (street, city, county, state, country, postal, phone) = location_data + parish = None + elif len(location_data) == 2: + ((street, city, county, state, country, postal, phone), parish) = location_data + else: + print "ERROR: what kind of location is this?", location_data + location = models.Location(street = street, + city = city, + county = county, + state = state, + country = country, + postal = postal, + phone = phone, + parish = parish, + order = order) + if field == "address": + location.address = obj + elif field == "place": + location.place = obj + else: + raise AttributeError("invalid field '%s' to attach to location" % + field) + location.save() + #obj.locations.add(location) + #obj.save() + + def add_place(self, data): + (handle, gid, title, long, lat, + main_loc, alt_location_list, + url_list, + media_list, + source_list, + note_list, + change, marker, private) = data + place = models.Place(handle=handle, gramps_id=gid, title=title, + long=long, lat=lat, last_changed=todate(change), + marker_type=models.get_type(models.MarkerType, marker), + private=private) + place.save() + + def add_place_detail(self, data): + (handle, gid, title, long, lat, + main_loc, alt_location_list, + url_list, + media_list, + source_list, + note_list, + change, marker, private) = data + place = models.Place.objects.get(handle=handle) + self.add_url_list("place", place, url_list) + self.add_media_ref_list(place, media_list) + self.add_source_ref_list(place, source_list) + self.add_note_list(place, note_list) + self.add_location("place", place, main_loc, 1) + count = 2 + for loc_data in alt_location_list: + self.add_location("place", place, loc_data, count) + count + 1 + + def add_media(self, data): + (handle, gid, path, mime, desc, + attribute_list, + source_list, + note_list, + change, + date, + marker, + private) = data + media = models.Media(handle=handle, gramps_id=gid, + path=path, mime=mime, + desc=desc, last_changed=todate(change), + marker_type=models.get_type(models.MarkerType, marker), + private=private) + self.add_date(media, date) + media.save() + + def add_media_detail(self, data): + (handle, gid, path, mime, desc, + attribute_list, + source_list, + note_list, + change, + date, + marker, + private) = data + media = models.Media.objects.get(handle=handle) + self.add_note_list(media, note_list) + self.add_source_ref_list(media, source_list) + self.add_attribute_list(media, attribute_list) + + def add_event(self, data): + (handle, gid, the_type, date, description, place_handle, + source_list, note_list, media_list, attribute_list, + change, marker, private) = data + event = models.Event(handle=handle, + gramps_id=gid, + event_type=models.get_type(models.EventType, the_type), + private=private, + marker_type=models.get_type(models.MarkerType, marker), + description=description, + last_changed=todate(change)) + self.add_date(event, date) + event.save() + + def add_event_detail(self, data): + (handle, gid, the_type, date, description, place_handle, + source_list, note_list, media_list, attribute_list, + change, marker, private) = data + event = models.Event.objects.get(handle=handle) + self.add_place_ref(event, place_handle) + self.add_note_list(event, note_list) + self.add_attribute_list(event, attribute_list) + self.add_media_ref_list(event, media_list) + self.add_source_ref_list(event, source_list) + diff --git a/src/gen/web/manage.py b/src/gen/web/manage.py new file mode 100755 index 000000000..5e78ea979 --- /dev/null +++ b/src/gen/web/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +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) diff --git a/src/gen/web/settings.py b/src/gen/web/settings.py new file mode 100644 index 000000000..f3e17b533 --- /dev/null +++ b/src/gen/web/settings.py @@ -0,0 +1,73 @@ +# Django settings for gramps project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ('admin', 'your_email@domain.com'), +) + +MANAGERS = ADMINS +DATABASE_ENGINE = 'sqlite3' +DATABASE_NAME = '/home/dblank/gramps/trunk/src/gen/web/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.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', +) + +ROOT_URLCONF = 'gen.web.urls' + +TEMPLATE_DIRS = ( + # Use absolute paths, not relative paths. + "/home/dblank/gramps/trunk/src/data/templates", +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.admin', + 'gen.web.grampsdb', +# 'django_extensions', +) + +# 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() + +# Views: (Nice name plural, /name/handle, Model Name) +VIEWS = [('People', 'person', 'Person'), + ('Families', 'family', 'Family'), + ('Events', 'event', 'Event'), + ('Notes', 'note', 'Note'), + ('Media', 'media', 'Media'), + ('Sources', 'source', 'Source'), + ('Places', 'place', 'Place'), + ('Repositories', 'repository', 'Repository'), + ] + diff --git a/src/gen/web/sortheaders.py b/src/gen/web/sortheaders.py new file mode 100644 index 000000000..f3e645e94 --- /dev/null +++ b/src/gen/web/sortheaders.py @@ -0,0 +1,232 @@ +# Author: insin +# Site: http://www.djangosnippets.org/snippets/308/ + +from django.core.paginator import InvalidPage, EmptyPage + +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' % '&'.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 = {} + + 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]) + + if letter not in chunks: + chunks[letter] = [] + + 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) diff --git a/src/gen/web/sqlite.db b/src/gen/web/sqlite.db new file mode 100644 index 000000000..b63619070 Binary files /dev/null and b/src/gen/web/sqlite.db differ diff --git a/src/gen/web/urls.py b/src/gen/web/urls.py new file mode 100644 index 000000000..3864bd0ee --- /dev/null +++ b/src/gen/web/urls.py @@ -0,0 +1,38 @@ +from django.conf.urls.defaults import * + +from django.contrib import admin +admin.autodiscover() + +from gen.web.grampsdb.views import (main_page, user_page, logout_page, + view, view_detail) + +urlpatterns = patterns('', + # Specific matches first: + (r'^admin/(.*)', admin.site.root), +) + +urlpatterns += patterns('', + # Static serves! DANGEROUS in production: + (r'^styles/(?P.*)$', 'django.views.static.serve', + {'document_root': + '/home/dblank/gramps/trunk/src/data', + 'show_indexes': + True}, + ), + (r'^images/(?P.*)$', 'django.views.static.serve', + {'document_root': + '/home/dblank/gramps/trunk/src/images', + '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(\w+))/$', view), + (r'^(?P(\w+))/(?P(\w+))/$', view_detail), +)