Changes

Jump to: navigation, search

Gramps-Connect: Developer Introduction

2,153 bytes added, 21:12, 21 October 2020
also abandoned see https://github.com/gramps-project/web-api which is active!
{{man warn|This has been abandoned|Please see https://github.com/gramps-project/web-api for future attempts}} Gramps developers are actively working on a version of Gramps thatruns on a web server, and displays one's family tree informationdynamically (as opposed to Gramps Narrated Web Report). This webapplication project is called Gramps-Connect. We have a workingdemonstration of this code available at http://gramps-connect.org. Itis written in Django, a Python web framework.
This page gives an introduction to Django for those that are already
familiar with Gramps data and Python. For more details on the specific Gramps Django interface, please see [[GEPS 013: Gramps Webapp]].
= Basic Django Structure =
The motivation and getting familiar with this project is provided in [[GEPS 013: Gramps Webapp]]. This dives into Django, from a getting started perspective. When you install and set up Django, all of the basic files are createdfor you with default settings. Django's[httphttps://docs.djangoproject.com/en/1.17/ online documentation] and the[http://groups.google.com/group/django-users google group] areexcellent references. To follow is an outline of Django's filestructure, some important basics, and specific examples of ways towork with the Gramps dataset to create webpages.
In working with Gramps' Django files, there are four files that you may find yourself editing:
# [http://{{Code Base}}gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/webwebapp/urls.py?view=log srcgramps/src/webwebapp/urls.py]# [http://gramps.svn.sourceforge.net/viewvc/{{Code Base}}gramps/trunk/src/webwebapp/grampsdb/views.py?view=log srcgramps/src/webwebapp/grampsdb/views.py]# [http://gramps.svn.sourceforge.net/viewvc/{{Code Base}}gramps/trunk/src/webwebapp/grampsdb/forms.py?view=log srcgramps/src/webwebapp/grampsdb/forms.py]# [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/{{Code Base}}data/templates src/data/templates/]
The template files have .html extensions and are found in thetemplates directory.
You will also find yourself referencing the
[http://{{Code Base}}gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/webwebapp/grampsdb/models.py?view=log srcgramps/webwebapp/grampsdb/models.py] file, which defines the databasestructure. This file models the Gramps data as it exists in thedesktop Gramps application and will not require alteration unless thedatabase structure is changed. To access data from the database, youwill often reference the models.py file and refer to the tables andfields it defines.
We will now go through each of these files, exploring the use and format of each.
'''==urls.py'''==
urls.py is the file in which you define of all of the website's urls
''urlpatterns'':
urlpatterns = patterns(''"", (r'^example/$', example_page),)
The above example maps the url '''www.mysite.com/example/''' (with or
can add more urls to the list as such:
urlpatterns += patterns(''"",
(r'^$', main_page),
(r'^login/$', login_page),
www.mysite.com/person/xxxxx/ where xxxxx is the record's primary key:
urlpatterns += patterns(''"", (r'^person/(\d+)/$',person_detail), )
The ''\d+'' in the regular expression matches one or more digits and
mapped the url (e.g., ''person_detail'').
'''==views.py'''==
The functions that you map to your urls will exist in the views.py
file. Each
[http://docs.djangoproject.com/en/1.17/topics/http/views/#topics-http-views view function] must take an http request object as its first parameter
and must return an http response object. The following view function
redirects the user to another page.
mydata = "the data I want to display"
return render_to_response('welcome.html', {'MyData':mydata})
</pre>
The above example passes data to the template welcome.html, which
<pre>
from webwebapp.grampsdb.models import *
def person_detail(request, ref):
n = 'unnamed person'
# work with data and pass information to template
return render_to_response('person_detail.html', {'Names':n, 'Handle': p.handle})
</pre>
fields like ''first'' and ''last name'' are displayed.
'''==Templates'''==
A template defines the html, but it is more than just an html file.
within {{ }}, and there are a number of filters that you can apply to
the data using pipes. There is a
[httphttps://docs.djangoproject.com/en/1.17/ref/templates/builtins/#ref-templates-builtins full list] of filters and tags on the Django website.
[http://docs.djangoproject.com/en/1.17/topics/templates/#template-inheritance Template inheritance] is a huge timesavertime saver. With the ''extends''
tag at the top of the file, a template inherits everything from its
parent. The parent template defines blocks that may be overridden by
names in the dataset.
'''==forms.py'''==
Sometimes you want website users to be able to enter and edit data.
This requires a form, and Django has a system for generating and
manipulating form data.
[http://docs.djangoproject.com/en/1.17/topics/forms/#topics-forms-index Forms] are defined in the forms module and view functions may use
these definitions to create forms to pass along to a template. A form
outlines the fields for the screen:
Most of the forms you will need to define will be based on data from
the database. To save time, Django can define a form for you
[http://docs.djangoproject.com/en/1.17/topics/forms/modelforms/#topics-forms-modelforms based on a model definition]. Be sure to include models.py to access
those definitions.
<pre>
from webwebapp.grampsdb.models import *
class PersonForm(forms.ModelForm):
than one record in a dataset. Django has a built-in abstraction layer
called
[http://docs.djangoproject.com/en/1.17/topics/forms/formsets/#topics-forms-formsets formsets] to create forms for datasets.
<pre>
An
[http://docs.djangoproject.com/en/1.17/topics/forms/modelforms/#inline-formsets inline formset] is a formset designed to help work with related
records. It isn't created with a class like a formset, but it does
automatically handle the foreign key fields between related tables.
Calling ''is_valid()'' on the form will run the clean functions and
return true if the data can be saved. Calling
[http://docs.djangoproject.com/en/1.17/topics/forms/modelforms/#the-save-method ''save()''] or ''save(commit=false)'' will also cause the clean
functions to run if they haven't already. The clean functions, when
they come across errors, fill error properties for each field and for
if psnform.is_valid(): # test validation rules
psnform.save()
# redirect after successful POST return HttpResponseRedirect('/success/')
else: # request to view record
psnform = PersonForm(instance=p)
return render_to_response('person_detail.html',{'PForm':psnform, 'URL':request.path})
</pre>
if psnform.is_valid(): # test validation rules
psnform.save()
# redirect after successful POST return HttpResponseRedirect('/success/')
else: # request for a blank form
psnform = PersonForm()
return render_to_response('person_detail.html', {'PForm':psnform, 'NFormset':nmformset,
'ScreenMsg':’Data was saved successfully’})
scrn_msg = ‘Please correct the errors listed below’ else: # request to view record psnform = PersonForm(instance=p, prefix='person') nmformset = NameInlineFormSet(instance=p, prefix='names') scrn_msg=’’
return render_to_response('person_detail.html',
{'PForm':psnform, 'NFormset':nmformset,
to format in paragraphs, ''.as_ul'' for an unordered list, and
''.as_table ''for a table. The following example
[http://docs.djangoproject.com/en/1.17/topics/forms/#looping-over-the-form-s-fields loops through the form's fields] to display each field (formatted as
an input), the field's label, the field's help text where applicable,
and the field's errors where not empty. A div refers to a custom css
<li>Last changed on {{PForm.last_changed.data}}</li>
</ul>
<input type="hidden" name="person-last_changed" id="id_person-last_changed" />
<input type="submit" value="Save" />
</form>
</pre>
[http://docs.djangoproject.com/en/1.17/topics/forms/modelforms/#using-the-formset-in-the-template Formsets] are just a bit more complicated, since the formset consists
of multiple forms. As with forms, formsets can handle themselves:
{% for field in form.visible_fields %}
<th>{{field.label_tag}}</th>
{% endfor %}
</tr>
{% endif %}
<tr><td> {# hidden fields for the form #}
{% for hidden in form.hidden_fields %}{{hidden }}{% endfor %}
</td></tr>
<tr> {# visible fields for the form #}
{% for field in form.visible_fields %}
<td>{{field}}</td>
{% endfor %}
</tr>
{% endif %}
<tr><td> {# hidden fields for the form #}
{% for hidden in form.hidden_fields %}{{hidden }}{% endfor %}
</td></tr>
<tr> {# visible fields for the form #}
{% for field in form.visible_fields %}
<td>{{field}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type=”submit” value=”Save” />
= A Note on Error-Checking Forms =
When it came to working with the name formset, we wanted to be able tocheck to ensure that there was at least one name for the person andthat one and only one name was marked as preferred. Since we were usingthe view function uses an
an inline formset, which is not defined as a class, error-checking
became problematic. After much trial and error, my the solution was to
write an error-checking function outside the form classes (but it made
sense to store it in the forms.py file). We soon discovered thatSince
cleaned_data is only populated after running the clean functions and
only if the data is clean, so the data is accessed in a slightly
different manner. A string is returned with an error message (or an
empty string if all passed).
= A Little Javascript =
One of the javascript functions we added to the display screen handleschanging the display icon for the ''private'' fields. We decided toso that thedisplay the lock and unlock image files from the Gramps application can be used in
place of the default checkbox used by Django for Boolean fields.
First, though, the child template needs to handle loading the
</pre>
The ''onclick '' actions call the javascript function ''clickPrivate'', which
is written into the base.html file. So that this function can be used
for any number of ''private'' fields on the screen, this function is
written so that it will take the ''id '' of the page element calling it.
<pre>
}
</pre>
 
 
= Privacy =
''Currently, these are developer notes.''
 
One of the most important aspects of an on-line version of Gramps is the protection of sensitive data. There are two categories of sensitive data:
 
# data marked private
# people currently alive
 
Gramps core has developed a layered proxy wrapper system for protection of this sensitive data for reports. Gramps-Connect can use this, but only in limited fashion as we wish to talk directly to the databases in certain places:
 
# running a search query
# editing data using Django Forms
 
Gramps-Connect uses name-replacement to protect living people, and skips over private data. This strategy is generally how many genealogy sites work, but is also technically the easiest/fastest to use. Living people are determined by using the function Utils.probably_alive. This function is recursive and generally expensive in the number of queries which must be executed to determine if a person does not have a death event. Thus, it is advantageous to not run that query until you know you want to show some details of that person. On the other hand, private data can be determined easily as they have a property that directly reflects this value. Running a query to skip over private records can be initiated easily:
 
<pre>
object_list = Family.objects \
.filter((Q(gramps_id__icontains=search) |
Q(family_rel_type__name__icontains=search) |
Q(father__name__surname__istartswith=search) |
Q(mother__name__surname__istartswith=search)) &
Q(private=False) &
Q(father__private=False) &
Q(mother__private=False)
) \
.order_by("gramps_id")
</pre>
 
It would make much sense for probably_alive to be cached and stored on the person record, once it is determined. If that occurred, then living people could be excluded in top-level queries as are private data.
 
[[Category:Developers/General]]
[[Category:GEPS]]
301
edits

Navigation menu