## Writing Extentions for gramps

2001
Donald N. Allingham

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found here.

Many of the names used by companies to distinguish their products and services are claimed as trademarks. Where those names appear in any GNOME documentation, and those trademarks are made aware to the members of the GNOME Documentation Project, the names have been printed in caps or initial caps.

This is version 1.0 of the Writing Extentions for gramps manual.

## Introduction

gramps was intended from the start to allow the user to extend it through a plugin system. Five types of plugins are supported - filters, reports, tools, import filters, and export filters. In a way, an export filter can be viewed as a special type of report, and an import filter can be viewed as a special type of tool.

All plugins are written in the python language.

### Filters

A filter is a plugin that be used to temporarily display or hide individuals in the People View. The filter is the simplest form of plugin, which only needs to determine if a person meets or fails to meet its criteria. It operates on a single person at a time.

Filters should never alter a database.

### Reports

A report is a plugin that generates output. The output may be in either a interactive, graphical form, or as an output file. Report plugins are passed a reference to the internal database and a reference to the active person, which allows the plugn to operate on a single person, the entire database, or anything in between.

Plugins that conform to the reportplugin interface appear in the Reports menu and in the Report Selection dialog box.

A report should never alter the database.

### Tools

A tool is a plugin that alters the database. It may perform something as small changing the case of some text to something as complex as merging redundant individuals. Tools plugins are passed a reference to the internal database, the active person, and a callback function. The callback function is used to notify the main program if it needs to update the display with any modified information.

Plugins that conform to the tool plugin interface appear in the Tools menu and in the Tool Selection dialog box.

A tool is allowed (and usually expected) to alter the database.

### Import Filters

An import filter is a plugin that adds information from another source to the database. It is similar to a tool, but is called differently to allow gramps to distinguish it from a tool.

Plugins that conform to the import filter calling syntax appear in the File Import menu.

An import filter is allowed to modify the database.

### Export Filters

An export filter is a plugin that translates the gramps database into the format expected by another program. Since it generates an output file, it is similar to a report generator. However, its calling syntax is different, so that gramps knows how to distiguish it from a report generator.

Plugins that conform to the export filter calling syntax appear in the File Export menu.

An export filter should not alter the database.

## Writing Filters

Users can create their own filters and add them to gramps. By adding the filter to the user's private filter directory (`~/.gramps/filters`), the filter will be automatically recognized the next time that the program is started.

### Creating a filter

Each filter is a class derived from the `Filter.Filter` class. The `__init__` task may be overridden, but if so, should call the `__init__` function on the `Filter.Filter` class. The parent class provides the variable `self.text`, which contains the text string passed as the qualifier. This string provides additional information provided by the user. For example, if the filter is used to match names, the qualifier would be used to provide the name that is being compared against.

All filter classes must define a `match` function. The function takes one argument (other than `self`), which is an object of type `Person` to compare against. The function should return a 1 if the person matches the filter, or a zero if the person does not.

Each filter must be registered, so that gramps knows about it. This is accomplished by calling the `Filter.register_filter` function. This function takes three arguments - the filter class, a description, and flag that indicates if the qualifier string is needed. The description string appears in the pull down interface within gramps, and helps the user choose the appropriate filter. The qualifier flag tells gramps whether or not the filter needs a qualifier string. If this flag is 0, gramps will disable the entry of a qualifier string.

#### Sample filter implementation

    import Filter
    import string

    # class definition

    class SubString(Filter.Filter):

        def match(self,person):
            name = person.getPrimaryName().getName()
            return string.find(name,self.text) >= 0

    Filter.register_filter(SubString,
                           description="Names that contain a substring",
    	               qualifier=1)

## Writing Reports

Users can create their own report generators and add them to gramps. By adding the report generator to the user's private plugin directory (`~/.gramps/plugins`), the report generator will be automatically recognized the next time that the program is started.

### Creating a report generator

Fewer restrictions are made on report generators than on filters. The report generator is passed the current gramps database and the active person. The generator needs to take special care to make sure that it does not alter the database in anyway.

A report generator is a function that takes two arguments — a database (of type `RelDataBase`) and the currently selected person (of type `Person`). When called, this task should generate the desired report.

This function's implementation can be as simple as generating output without the user's intervention, or it could display a graphical interface to allow the user to select options and customize a report.

As with filters, the report generator must be registered before gramps will understand it. The report generator is registered using the `Plugins.register_report`. This function takes five arguments.

- The report generation task This task that generates the report.

- The report category The category in which the report is grouped in the Reports menu and in the Report Selection dialog.

- The report name The name of the report.

- A text description of the report The description appears in the report selection tool to provide the user with a description of what the tools does.

- A graphic logo in XPM format This may be either a path to a filename, or a list of strings containting the XPM data. If a filename is specified, care must be taken to make sure the file location is relocatable and can be determined at runtime.

While only the task and report name are required, it is recommended to provide all five parameters.

#### Sample report implementation

    import Plugins

    def report(database,person):
       ... actual code ...

    Plugins.register_report(
        task=report,
        category="Category",
        name="Report Name",
        description="A text descripition of the report generator",
        xpm="%s/myfile.xpm" % os.path.dirname(__file__)
    )

### A little help - Format Interfaces

gramps provides some help with writing reports. Several generic python classes exist that aid in the writing of report generators. These classes provide an abstract interface for a type of document, such as a drawing, word processor document, or a spreadsheet. From these core classes, gramps derives interfaces to various document formats. This means that by coding to the generic word processing class (`TextDoc`), a report generator can instant access to multiple file formats (such as HTML, OpenOffice, and AbiWord).

This scheme of deriving a output format from a generic base class also makes it easier to add new formats. Creating a new derivied class targeting a different format (such as KWord or LaTeX) makes it easy for existing report generators to use the new formats.

## Writing Tools

Users can create their own tools and add them to gramps. By adding the tool to the user's private plugin directory (`~/.gramps/plugins`), the tool will be automatically recognized the next time that gramps is started.

Unlike a report generator, a tool is allowed to modify the database. The tool is passed the current gramps database, the active person, and a callback function. The callback function should be called with a non-zero argument upon completion of the tool if the database has been altered.

As with filters and report generators, tools must be registered before gramps will understand it. The tool is registered using the `Plugins.register_tool`. This function takes four arguments.

- The tool task This task that executes the tool.

- The tool category The category in which the tool is grouped in the Tools menu and in the Tool Selection dialog.

- The tool name The name of the tool.

- A text description of the tool The description appears in the Tool Selection dialog to provide the user with a description of what the tool does.

While only the task and report name are required, it is recommended to provide all five parameters.

### Sample tool implementation

    import Plugins

    def tool(database,person,callback):
       ... actual code ...
       callback(1)

    Plugins.register_tool(
        task=tool,
        category="Category",
        name="Tool Name",
        description="A text descripition of the tool"
    )

## Writing Import Filters

Import filters are similar to tools, since they are allowed to modify the databases. An import filter is a task that accepts three arguments — a database, the filename of the file that is to be imported, and a callback function.

The database may or may not have data already in it. The import filter cannot assume that data neither already exists nor that the database is empty.

The callback function is different from the callback function used for tools. The import filter's callback function is used to indicate progress and update the status bar during the import process. The function takes a value between 0.0 and 1.0, where 0.0 represents the start of the import and 1.0 represents the completion of the import.

As with the other plugin types, an import filter must be registered with gramps. This is accomplished by calling the `Plugins.register_import` task. The `Plugins.register_import` accepts two arguments — the function the performs the import and a string providing a brief description. This description is used as the menu entry under the File Import menu.

### Sample Import Implementation

    import Plugins

    def gedcom_import(database,filename,callback):
        ... actual code ...

    Plugins.register_import(gedcom_import,"GEDCOM import")

## Writing Export Filters

Export filters are similar to report generators. They are not allowed to modify the database. An export filter accepts three arguments — a database, the filename of the file that is to be written, and a callback function.

The callback function is indentical from the callback function used for import filters. The export filter's callback function is used to indicate progress and update the status bar during the export process. The function takes a value between 0.0 and 1.0, where 0.0 represents the start of the export and 1.0 represents the completion of the export.

As with the other plugin types, an export filter must be registered with gramps. This is accomplished by calling the `Plugins.register_export` task. The `Plugins.register_export` accepts two arguments — the function the performs the import and a string providing a brief description. This description is used as the menu entry under the File Export menu.

### Sample Export Implementation

    import Plugins

    def gedcom_export(database,filename,callback):
        ... actual code ...

    Plugins.register_export(gedcom_export,"GEDCOM export")

## Common tasks

While this manual does not document the gramps database interface, this section shows a few common tasks.

### Printing names of people

This example shows how to display the name of people in the database. It assumes that the database is called `db`. To get a list of people, it calls the `getPersonMap` method, which returns a map of gramps ID to `Person` objects. Calling the `valus` method of the returned map returns a list of people. For each person, the primary name is extracted, and then the `Name` object's `getName` method is called to build a presentable name from the individual name components.

#### Displaying names

    for person in db.getPersonMap().values():
        name = person.getPrimaryName()
        print name.getName()

### Displaying the events of person

This example shows how to display the public events associated with a person. It assumes that the person is called `person`.

#### Displaying Event Information

    for event in person.getEventList():
        if event.getPrivacy() == 0:
            print "Event:",event.getName()
    	print "Date:",event.getDate()
    	print "Place:",event.getPlaceName()

### Print the members of each family

This example shows how to display the parents and children of each family in the database. It assumes that the database is called `db`.

#### Displaying Family Information

    for family in db.getFamilyMap().values:
        print "-------------------"
        print "Family ID:",family.getId()
        father = family.getFather()
        if father != None:
            print "Father:",father.getPrimaryName().getName()
        mother = family.getMother()
        if mother != None:
            print "Mother:",mother.getPrimaryName().getName()
        for child in family.getChildList():
            print "Child:",child.getPrimaryName().getName()

### Display the marriages/relationships of a person

This example shows how to display the families and relationships in which the person is considered a spouse or parent. It assumes that the person is called `person`.

Relationships between people can be complex. Because someone is male, does not necessarily mean that the person will be considered the "Father" of a relationship. In relationships of type "Partners", the "father" and "mother" of the relationship should be of the same gender. So to determine the spouse of a person, it is usually best to compare the person against what is returned by `getFather` and `getMother` to find the one that is not equal. It should also be noted that the `getFather` and `getMother` methods will return None if noone has been associated with that role in the family.

#### Displaying Relationship Information

    for family in person.getFamilyList():
        print "-------------------"
        print "Family ID:",family.getId()
        print "Relationship Type:",family.getRelationship()
        father = family.getFather()
        if father != None and father != person:
            print "Spouse:",father.getPrimaryName().getName()
        mother = family.getMother()
        if mother != None and mother != person:
            print "Spouse:",mother.getPrimaryName().getName()

## Authors

gramps was written by Don Allingham (dallingham@users.sourceforge.net). To find more information about gramps, please visit the gramps web page.

This manual was written by Don Allingham (dallingham@users.sourceforge.net).

## License

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.

A copy of the GNU General Public License is included as an appendix to the GNOME Users Guide. You may also obtain a copy of the GNU General Public License from the Free Software Foundation by visiting their Web site or by writing to

        Free Software Foundation, Inc.  59 Temple Place -
        Suite 330 Boston, MA
        02111-1307 USA