New files for gramps webapp; src/data/templates contains html templates, and src/gen/web contains gramps-Django interface
svn: r13542
This commit is contained in:
parent
b7327e679a
commit
4ab1467845
55
src/data/templates/gramps-base.html
Normal file
55
src/data/templates/gramps-base.html
Normal file
@ -0,0 +1,55 @@
|
||||
{% load my_tags %}
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="" lang="">
|
||||
<head>
|
||||
<title>{% block title %}GRAMPS Connect{% endblock %}</title>
|
||||
{% block meta %}
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="generator" content="GRAMPS 3.2.0-0.SVN12859M http://gramps-project.org/" />
|
||||
<meta name="author" content="" />
|
||||
{% endblock %}
|
||||
<link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
|
||||
{% block css %}
|
||||
<link media="screen" href="/styles/behaviour.css" type="text/css" rel="stylesheet" />
|
||||
<link media="screen" href="/styles/Web_Alphabet-Horizontal.css" type="text/css" rel="stylesheet" />
|
||||
<link media="screen" href="/styles/Web_Mainz.css" type="text/css" rel="stylesheet" />
|
||||
<link media="print" href="/styles/Web_Print-Default.css" type="text/css" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body id= "NarrativeWeb">
|
||||
<div id="header">
|
||||
<h1 id="SiteTitle">{% block heading %}GRAMPS Connect{% endblock %}</h1>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
{% block navigation %}
|
||||
<ul>
|
||||
<li class="{{ cview|currentSection:"home" }}"><a href="/">Home</a></li>
|
||||
{% for view in views %}
|
||||
<li class="{{ cview|currentSection:view.1 }}"><a href="/{{view.1}}/">{{view.0}}</a></li>
|
||||
{% endfor %}
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="/logout">Logout</a></li>
|
||||
{% if user.is_superuser %}
|
||||
<li><a href="/admin">Admin</a></li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li><a href="/login/">Login</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="grampsweb">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
{% block footer %}(c) 2009 <a href="http://www.gramps-project.org/">www.gramps-project.net</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
26
src/data/templates/main_page.html
Normal file
26
src/data/templates/main_page.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "gramps-base.html" %}
|
||||
|
||||
{% block title %}GRAMPS Connect - main page {% endblock %}
|
||||
{% block heading %}GRAMPS - main page {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p id="description">Welcome to GRAMPS Connect, a new web-based collaboration tool.
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
You are now logged in
|
||||
as <a href="/user/{{user.username}}">{{user.username}}</a>.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<p id="description">
|
||||
Database information:
|
||||
<ul>
|
||||
{% for view in views %}
|
||||
<li><a href="/{{view.1}}">{{view.0}}</a> ({{view.2}} records)</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
31
src/data/templates/person_detail-one name.html
Normal file
31
src/data/templates/person_detail-one name.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block windowtitle %}Person Details{% endblock %}
|
||||
|
||||
{% block pagetitle %}{{Pname}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form name="input" action={{URL}} method="post">
|
||||
<table cellspacing = "2">
|
||||
<tr><td colspan = "2">Preferred Name</td></tr>
|
||||
{% for field in NForm %}
|
||||
<div class="fieldWrapper"><tr>
|
||||
<td width="10"></td>
|
||||
<td>{{ field.label_tag }}: </td>
|
||||
<td width="300">{{ field }}<p>{{ field.help_text }}</p></td>
|
||||
<td>{{ field.errors }}</td>
|
||||
</tr></div>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<table cellspacing = "2">
|
||||
{% for field in PForm %}
|
||||
<div class="fieldWrapper"><tr>
|
||||
<td>{{ field.label_tag }}: </td>
|
||||
<td width="300">{{ field }}<p>{{ field.help_text }}</p></td>
|
||||
<td>{{ field.errors }}</td>
|
||||
</tr></div>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="submit" value="Save & Exit" />
|
||||
</form>
|
||||
{% endblock %}
|
69
src/data/templates/person_detail.html
Normal file
69
src/data/templates/person_detail.html
Normal file
@ -0,0 +1,69 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block windowtitle %}Person Details{% endblock %}
|
||||
|
||||
{% block pagetitle %}{{Pname}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form name="input" action={{URL}} method="post">
|
||||
{{ NForm.management_form }}
|
||||
Names (<a id="displayText" href="javascript:toggle('nameset','show');">hide</a>)
|
||||
<div id="nameset" style="display: block">
|
||||
<table class="nicetable">
|
||||
<tr>
|
||||
<td>Preferred Name?</td>
|
||||
<td>Prefix</td>
|
||||
<td>First Name</td>
|
||||
<td>Surname</td>
|
||||
<td>Suffix</td>
|
||||
<td>Type of Name</td>
|
||||
<td>Delete</td>
|
||||
</tr>
|
||||
{% for form in NForm.forms %}
|
||||
<tr>
|
||||
{% for field in form %}
|
||||
<td><p>{{ field }}</p>
|
||||
<div id="errmsg"><p>{{ field.errors }}</p></div></td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr><td colspan="7"><div id="errmsg">{{ NamesetError }}</div></td></tr>
|
||||
</table></div>
|
||||
|
||||
<table class="nicetable">
|
||||
<tr>
|
||||
<td>Reference ID: {{PForm.gramps_id}}</td>
|
||||
<td>{{PForm.private}} Private </td>
|
||||
<td>Last Changed: {{PLastChanged}}
|
||||
<div id="errmsg"><p>{{PForm.last_changed.errors}}</p></div></td>
|
||||
</tr><tr>
|
||||
<td>Marker: {{PForm.marker_type}}
|
||||
<div id="errmsg"><p>{{PForm.marker_type.errors}}</p></div></td>
|
||||
<td>Gender: {{PForm.gender_type}}
|
||||
<div id="errmsg"><p>{{PForm.gender_type.errors}}</p></div></td>
|
||||
</tr><tr>
|
||||
<td><p>Parent in these families:
|
||||
(<a id="displayText" href="javascript:toggle('families','Edit');">
|
||||
Edit</a>)</p><ul>{% for family in ParentF %}
|
||||
<li>{{family}}</li>
|
||||
{% endfor %}</ul></td>
|
||||
<td><p>Child in these families:
|
||||
(<a id="displayText" href="javascript:toggle('pfamilies','Edit');"> Edit</a>)</p><ul>{% for family in ChildF %}
|
||||
<li>{{family}}</li>
|
||||
{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="nicetable">
|
||||
<tr><td width = "300"><div id="families" style="display: none">
|
||||
<p>{{PForm.families}}</p>
|
||||
<p>{{PForm.families.help_text}}</p>
|
||||
<p>{{PForm.famlies.errors}}</p></div></td>
|
||||
|
||||
<td width = "300"><div id="pfamilies" style="display: none">
|
||||
<p>{{PForm.parent_families}}</p>
|
||||
<p>{{PForm.parent_families.help_text}}</p>
|
||||
<p>{{PForm.parent_famlies.errors}}</p></div></td></tr>
|
||||
</table>
|
||||
<input type="submit" value="Save & Exit" />
|
||||
</form>
|
||||
{% endblock %}
|
9
src/data/templates/places.html
Normal file
9
src/data/templates/places.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block windowtitle %}Places{% endblock %}
|
||||
|
||||
{% block pagetitle %}Places{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
Congratulations, you found Places
|
||||
{% endblock %}
|
49
src/data/templates/pname.html
Normal file
49
src/data/templates/pname.html
Normal file
@ -0,0 +1,49 @@
|
||||
{{ NForm.management_form }}
|
||||
<table cellspacing = "2">
|
||||
<tr><td colspan = "2">Names:</td></tr>
|
||||
<tr><td width="10"></td>
|
||||
<td><label>Preferred Name?</label></td>
|
||||
<td width="50"><label>Prefix</label></td>
|
||||
<td><label>First Name</label></td>
|
||||
<td><label>Surname</label></td>
|
||||
<td><label>Suffix</label></td>
|
||||
<td><label>Type of Name</label></td>
|
||||
<td><label>Delete</label></td>
|
||||
</tr>
|
||||
{% for form in NForm.forms %}
|
||||
<div class="fieldWrapper"><tr>
|
||||
<td width="10"></td>
|
||||
{% for field in form %}
|
||||
<td align="center">{{ field }}</td>
|
||||
{% endfor %}
|
||||
</tr></div>
|
||||
<tr><td colspan="5>{{form.errors}}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% for field in PForm %}
|
||||
<div class="fieldWrapper"><tr>
|
||||
<td>{{ field.label_tag }}: </td>
|
||||
<td width="300">{{ field }}<p>{{ field.help_text }}</p></td>
|
||||
<td>{{ field.errors }}</td>
|
||||
</tr></div>
|
||||
{% endfor %}
|
||||
|
||||
{{ NForm.management_form }}
|
||||
<table>
|
||||
<td>Preferred Name?</td>
|
||||
<td>Prefix</td>
|
||||
<td>First Name</td>
|
||||
<td>Surname</td>
|
||||
<td>Suffix</td>
|
||||
<td>Type of Name</td>
|
||||
<td>Delete</td>
|
||||
</tr>
|
||||
{% for form in NForm.forms %}
|
||||
{% for field in form %}
|
||||
<td align="left">{{ field }}<p>{{ field.errors }}</p></td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr><td colspan="7"></td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
35
src/data/templates/registration/login.html
Normal file
35
src/data/templates/registration/login.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends "gramps-base.html" %}
|
||||
|
||||
{% block title %}GRAMPS Connect - login {% endblock %}
|
||||
{% block heading %}GRAMPS - login {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>User Login</h2>
|
||||
{% if form.errors %}
|
||||
<p id="description">Your username or password were not valid. Please try again.</p>
|
||||
{% else %}
|
||||
<p id="description">Enter your login ID and password below. </p>
|
||||
{% endif %}
|
||||
<form method="post" action=".">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="id_username">Username: </label>
|
||||
</td>
|
||||
<td>
|
||||
{{form.username}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="id_password">Password: </label>
|
||||
</td>
|
||||
<td>
|
||||
{{form.password}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="hidden" name="next" value="/" />
|
||||
<input type="submit" value="Login" />
|
||||
</form>
|
||||
{% endblock %}
|
9
src/data/templates/successful_data_change.html
Normal file
9
src/data/templates/successful_data_change.html
Normal file
@ -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 %}
|
5
src/data/templates/table_header.html
Normal file
5
src/data/templates/table_header.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% for header in headers %}<th{{ header.class_attr }}>
|
||||
{% if header.sortable %}<a href="{{ header.url|escape }}">{% endif %}
|
||||
{{ header.text }}
|
||||
{% if header.sortable %}</a>{% endif %}
|
||||
</th>{% endfor %}
|
20
src/data/templates/user_page.html
Normal file
20
src/data/templates/user_page.html
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends "gramps-base.html" %}
|
||||
|
||||
{% block title %}GRAMPS Connect - user page {% endblock %}
|
||||
{% block heading %}GRAMPS - user page {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p id="description">Details for <b>{{user.first_name}} {{user.last_name}}</b> ({{user.username}}):</p>
|
||||
|
||||
<p id="description">
|
||||
<ul>
|
||||
<li>User name: <b>{{user.username}}</b></li>
|
||||
<li>Email: <b><a href="mailto:{{user.email}}">{{user.email}}></a></b></li>
|
||||
<li>Superuser?: <b>{{user.is_superuser}}</b></li>
|
||||
<li>Last login: <b>{{user.last_login}}</b></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
10
src/data/templates/view_detail_page.html
Normal file
10
src/data/templates/view_detail_page.html
Normal file
@ -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 %}
|
||||
|
34
src/data/templates/view_events.html
Normal file
34
src/data/templates/view_events.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "view_page.html" %}
|
||||
{% load my_tags %}
|
||||
|
||||
{% block table_data %}
|
||||
|
||||
<table cellspacing="0" class="infolist surname">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item #</th>
|
||||
<th>ID</th>
|
||||
<th>Father</th>
|
||||
<th>Mother</th>
|
||||
<th>Relationship</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for family in page.object_list %}
|
||||
<tr class="{% cycle odd,even %}">
|
||||
<td>{{ forloop.counter|row_count:page }}</td>
|
||||
<td><a href="/{{view}}/{{family.gramps_id|escape}}" class="noThumb"><span class="grampsid">[{{family.gramps_id}}]</span></a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.father.name_set|make_name:user}}</a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.mother.name_set|make_name:user}}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.family_rel_type|escape}}</a>
|
||||
{% else %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">[Private]</a>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
34
src/data/templates/view_family.html
Normal file
34
src/data/templates/view_family.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "view_page.html" %}
|
||||
{% load my_tags %}
|
||||
|
||||
{% block table_data %}
|
||||
|
||||
<table cellspacing="0" class="infolist surname">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item #</th>
|
||||
<th>ID</th>
|
||||
<th>Father</th>
|
||||
<th>Mother</th>
|
||||
<th>Relationship</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for family in page.object_list %}
|
||||
<tr class="{% cycle odd,even %}">
|
||||
<td>{{ forloop.counter|row_count:page }}</td>
|
||||
<td><a href="/{{view}}/{{family.gramps_id|escape}}" class="noThumb"><span class="grampsid">[{{family.gramps_id}}]</span></a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.father.name_set|make_name:user}}</a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.mother.name_set|make_name:user}}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.family_rel_type|escape}}</a>
|
||||
{% else %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">[Private]</a>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
13
src/data/templates/view_navigation.html
Normal file
13
src/data/templates/view_navigation.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% load my_tags %}
|
||||
<table cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
{% table_header %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for view in view_list %}<tr class="{% cycle odd,even %}">
|
||||
<td><a href="/{{view.name|lower}}/">{{ view.name|escape }}</a></td>
|
||||
</tr>{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
81
src/data/templates/view_page.html
Normal file
81
src/data/templates/view_page.html
Normal file
@ -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 %}
|
||||
|
||||
<p id="description">
|
||||
<form>
|
||||
<input type="submit" value="Search:"></submit>
|
||||
<input name="search" type="input" size="50" value="{{search}}"></input>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% ifequal page.number 1 %}
|
||||
[first]
|
||||
{% else %}
|
||||
[<a href="?page=1{{search_query}}">first</a>]
|
||||
{% endifequal %}
|
||||
{% if page.has_previous %}
|
||||
[<a href="?page={{page.previous_page_number}}{{search_query}}">previous</a>]
|
||||
{% else %}
|
||||
[previous]
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ page.number }} of {{ page.paginator.num_pages }}
|
||||
</span>
|
||||
|
||||
{% if page.has_next %}
|
||||
[<a href="?page={{ page.next_page_number }}{{search_query}}">next</a>]
|
||||
{% else %}
|
||||
[next]
|
||||
{% endif %}
|
||||
{% ifequal page.number page.paginator.num_pages %}
|
||||
[last]
|
||||
{% else %}
|
||||
[<a href="?page={{page.paginator.num_pages}}{{search_query}}">last</a>]
|
||||
{% endifequal %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
{% block table_data %} TABLE DATA GOES HERE {% endblock %}
|
||||
<table></table>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% ifequal page.number 1 %}
|
||||
[first]
|
||||
{% else %}
|
||||
[<a href="?page=1{{search_query}}">first</a>]
|
||||
{% endifequal %}
|
||||
{% if page.has_previous %}
|
||||
[<a href="?page={{page.previous_page_number}}{{search_query}}">previous</a>]
|
||||
{% else %}
|
||||
[previous]
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ page.number }} of {{ page.paginator.num_pages }}
|
||||
</span>
|
||||
|
||||
{% if page.has_next %}
|
||||
[<a href="?page={{ page.next_page_number }}{{search_query}}">next</a>]
|
||||
{% else %}
|
||||
[next]
|
||||
{% endif %}
|
||||
{% ifequal page.number page.paginator.num_pages %}
|
||||
[last]
|
||||
{% else %}
|
||||
[<a href="?page={{page.paginator.num_pages}}{{search_query}}">last</a>]
|
||||
{% endifequal %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
41
src/data/templates/view_person.html
Normal file
41
src/data/templates/view_person.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends "view_page.html" %}
|
||||
{% load my_tags %}
|
||||
|
||||
{% block table_data %}
|
||||
|
||||
<table cellspacing="0" class="infolist IndividualList">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item #</th>
|
||||
<th>Name</th>
|
||||
<th>ID</th>
|
||||
<th>Gender</th>
|
||||
<th>Birth Date</th>
|
||||
<!-- <td>Birth Place</td> -->
|
||||
<!-- <td>Death Place</td> -->
|
||||
<th>Death Date</th>
|
||||
<!-- <td>Spouse</td> -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name in page.object_list %}
|
||||
<tr class="{% cycle odd,even %}">
|
||||
<td>{{ forloop.counter|row_count:page }}</td>
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}/" class="noThumb">{{name|make_name:user}}</a>
|
||||
</td>
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}" class="grampsid">[{{name.person.gramps_id|escape}}]</a></td>
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}" class="noThumb">{{name.person.gender_type|escape}}</a></td>
|
||||
{% if user.is_authenticated %}
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}" class="noThumb">{{name.person|person_get_birth_date}}</a></td>
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}" class="noThumb">{{name.person|person_get_death_date}}</a></td>
|
||||
{% else %}
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}/" class="noThumb">[Private]</a>
|
||||
<td><a href="/{{view}}/{{name.person.handle|escape}}/" class="noThumb">[Private]</a>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
34
src/data/templates/view_repository.html
Normal file
34
src/data/templates/view_repository.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% extends "view_page.html" %}
|
||||
{% load my_tags %}
|
||||
|
||||
{% block table_data %}
|
||||
|
||||
<table cellspacing="0" class="infolist surname">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item #</th>
|
||||
<th>ID</th>
|
||||
<th>Father</th>
|
||||
<th>Mother</th>
|
||||
<th>Relationship</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for family in page.object_list %}
|
||||
<tr class="{% cycle odd,even %}">
|
||||
<td>{{ forloop.counter|row_count:page }}</td>
|
||||
<td><a href="/{{view}}/{{family.gramps_id|escape}}" class="noThumb"><span class="grampsid">[{{family.gramps_id}}]</span></a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.father.name_set|make_name:user}}</a>
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.mother.name_set|make_name:user}}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">{{family.family_rel_type|escape}}</a>
|
||||
{% else %}
|
||||
<td><a href="/{{view}}/{{family.handle|escape}}">[Private]</a>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
|
39
src/gen/web/Makefile
Normal file
39
src/gen/web/Makefile
Normal file
@ -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
|
209
src/gen/web/README
Normal file
209
src/gen/web/README
Normal file
@ -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
|
||||
|
||||
|
0
src/gen/web/__init__.py
Normal file
0
src/gen/web/__init__.py
Normal file
249
src/gen/web/djangodb.py
Normal file
249
src/gen/web/djangodb.py
Normal file
@ -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
|
||||
|
||||
|
69
src/gen/web/forms.py
Normal file
69
src/gen/web/forms.py
Normal file
@ -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"
|
0
src/gen/web/grampsdb/__init__.py
Normal file
0
src/gen/web/grampsdb/__init__.py
Normal file
6
src/gen/web/grampsdb/admin.py
Normal file
6
src/gen/web/grampsdb/admin.py
Normal file
@ -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])
|
||||
|
873
src/gen/web/grampsdb/models.py
Normal file
873
src/gen/web/grampsdb/models.py
Normal file
@ -0,0 +1,873 @@
|
||||
# Gramps - a GTK+/GNOME based genealogy program
|
||||
#
|
||||
# Copyright (C) 2009 B. Malengier <benny.malengier@gmail.com>
|
||||
# Copyright (C) 2009 Douglas S. Blank <doug.blank@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
|
||||
"""
|
||||
All of the models for the grampsdb Django data schema.
|
||||
This requires initial data for all of the Types, which
|
||||
is loaded by the fixtures/initial_data.json, which is
|
||||
created by init.py.
|
||||
"""
|
||||
|
||||
_DEBUG = True
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
|
||||
from gen.lib.date import Date as GDate, Today
|
||||
from Utils import create_id, create_uid
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#
|
||||
# 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()
|
0
src/gen/web/grampsdb/templatetags/__init__.py
Normal file
0
src/gen/web/grampsdb/templatetags/__init__.py
Normal file
148
src/gen/web/grampsdb/templatetags/my_tags.py
Normal file
148
src/gen/web/grampsdb/templatetags/my_tags.py
Normal file
@ -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)
|
125
src/gen/web/grampsdb/views.py
Normal file
125
src/gen/web/grampsdb/views.py
Normal file
@ -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)
|
92
src/gen/web/init.py
Normal file
92
src/gen/web/init.py
Normal file
@ -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 "]"
|
11
src/gen/web/init_gramps.py
Normal file
11
src/gen/web/init_gramps.py
Normal file
@ -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")
|
1230
src/gen/web/libdjango.py
Normal file
1230
src/gen/web/libdjango.py
Normal file
File diff suppressed because it is too large
Load Diff
11
src/gen/web/manage.py
Executable file
11
src/gen/web/manage.py
Executable file
@ -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)
|
73
src/gen/web/settings.py
Normal file
73
src/gen/web/settings.py
Normal file
@ -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'),
|
||||
]
|
||||
|
232
src/gen/web/sortheaders.py
Normal file
232
src/gen/web/sortheaders.py
Normal file
@ -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)
|
BIN
src/gen/web/sqlite.db
Normal file
BIN
src/gen/web/sqlite.db
Normal file
Binary file not shown.
38
src/gen/web/urls.py
Normal file
38
src/gen/web/urls.py
Normal file
@ -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<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root':
|
||||
'/home/dblank/gramps/trunk/src/data',
|
||||
'show_indexes':
|
||||
True},
|
||||
),
|
||||
(r'^images/(?P<path>.*)$', '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<view>(\w+))/$', view),
|
||||
(r'^(?P<view>(\w+))/(?P<handle>(\w+))/$', view_detail),
|
||||
)
|
Loading…
Reference in New Issue
Block a user