This tutorial covers the basics of writing a simple report using the Gramps report infrastructure. It covers the process of handling options, building a document and creating the report.

−

This tutorial covers the basics of writing a simple report using the GRAMPS report infrastructure. It covers the process of handling options, building a document and creating the report.

+

The goals of this report are to create a database summary report. It will include the following information in the report:

The goals of this report are to create a database summary report. It will include the following information in the report:

Line 11:

Line 10:

* The most common surname

* The most common surname

−

As of version 3.2, there is also a [[Simple Access API|simple access]] database [http://www.gramps-project.org/docs/ API] available, with accompanying [[Quick Reports]], [[Gramplets]] and [[Addons_Development|Addons]].

+

As of Gramps version 3.2, there is also a [[Simple Access API|simple access]] database [http://www.gramps-project.org/docs/ API] available, with accompanying [[Quick Views]], [[Gramplets]] and [[Addons development|Addons]].

==Overview==

==Overview==

−

Before going into details, it is useful to note that the report should have three basic parts. As explained on the [[Addons_Development|Addons]] page, these go into two different files: the source code file (e.g.: report.py) and a Gramps Plugin Registration file (e.g.: report.gpr.py).

+

Before going into details, it is useful to note that the report should have two basic parts. As explained on the [[Addons_Development|Addons]] page, these go into two different files: the source code file (e.g.: report.py) and a Gramps Plugin Registration file (e.g.: report.gpr.py).

===report.py===

===report.py===

−

;Report class : This is the code that takes data from the GRAMPS database and organizes it into the document structure. This structure can later be printed, viewed, or written into a file in a variety of formats. This class uses the [http://packages.python.org/Gramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen] interface to abstract away the output format details.

+

;Report class : This is the code that takes data from the Gramps database and organizes it into the document structure. This structure can later be printed, viewed, or written into a file in a variety of formats. This class uses the [http://packages.python.org/Gramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen] interface to abstract away the output format details.

;Options class : This is the code that provides means to obtain options necessary for the report using a variety of available mechanisms.

;Options class : This is the code that provides means to obtain options necessary for the report using a variety of available mechanisms.

===report.gpr.py===

===report.gpr.py===

−

;Registration statement : This initializes the report by a single call to the <tt>register()</tt> function. It is trivial, but without it your report will not become available to GRAMPS, even if it is otherwise perfectly written.

+

;Registration statement : This initializes the report by a single call to the <tt>register()</tt> function. It is trivial, but without it your report will not become available to Gramps, even if it is otherwise perfectly written.

−

A report can potentially be generated as a standalone report, as a GRAMPS Book item, and as a command line report. The registration determines which modes are enabled for a given report. The report class does not have to know anything about the mode. The options class is there to provide options interface for all available modes.

+

A report can potentially be generated as a standalone report, as a Gramps Book item, and as a command line report. The registration determines which modes are enabled for a given report. The report class does not have to know anything about the mode. The options class is there to provide options interface for all available modes.

==Document interface==

==Document interface==

−

GRAMPS attempts to abstract the output document format away from the report. By coding to the [http://packages.python.org/Gramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen] class, the report can generate its output in the format desired by the end user. The document passed to the report (<tt>self.doc</tt>) could represent an HTML, OpenDocument, PDF or any of the other formats supported by the user. The report does not have to concern itself with the output format details, since all details are handled by the document object.

+

Gramps attempts to abstract the output document format away from the report. By coding to the [http://packages.python.org/Gramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen] class, the report can generate its output in the format desired by the end user. The document passed to the report (<tt>self.doc</tt>) could represent an HTML, OpenDocument, PDF or any of the other formats supported by the user. The report does not have to concern itself with the output format details, since all details are handled by the document object.

A document is composed of paragraphs, tables, and graphics objects. Tables and graphics objects will not be covered in this tutorial.

A document is composed of paragraphs, tables, and graphics objects. Tables and graphics objects will not be covered in this tutorial.

Line 36:

Line 35:

Paragraph and font styles are defined in the <tt>make_default_style()</tt> function of the options class. The paragraphs are grouped into a <tt>StyleSheet</tt>, which the <tt>make_default_style()</tt> function defines. For the example report (<tt>DbSummary</tt>), the paragraph styles are defined as below:

Paragraph and font styles are defined in the <tt>make_default_style()</tt> function of the options class. The paragraphs are grouped into a <tt>StyleSheet</tt>, which the <tt>make_default_style()</tt> function defines. For the example report (<tt>DbSummary</tt>), the paragraph styles are defined as below:

<pre>

<pre>

−

def make_default_style(self, default_style):

+

def make_default_style(self, default_style):

−

# Define the title paragraph, named 'DBS-Title', which uses a

+

# Define the title paragraph, named 'DBS-Title', which uses a

−

# 18 point, bold Sans Serif font with a paragraph that is centered

+

# 18 point, bold Sans Serif font with a paragraph that is centered

−

font = docgen.FontStyle()

+

font = docgen.FontStyle()

−

font.set_size(18)

+

font.set_size(18)

−

font.set_type_face(docgen.FONT_SANS_SERIF)

+

font.set_type_face(docgen.FONT_SANS_SERIF)

−

font.set_bold(True)

+

font.set_bold(True)

−

para = docgen.ParagraphStyle()

+

para = docgen.ParagraphStyle()

−

para.set_header_level(1)

+

para.set_header_level(1)

−

para.set_alignment(docgen.PARA_ALIGN_CENTER)

+

para.set_alignment(docgen.PARA_ALIGN_CENTER)

−

para.set_font(font)

+

para.set_font(font)

−

para.set_description(_('The style used for the title of the page.'))

+

para.set_description(_('The style used for the title of the page.'))

−

default_style.add_style('DBS-Title',para)

+

default_style.add_style('DBS-Title',para)

−

# Define the normal paragraph, named 'DBS-Normal', which uses a

+

# Define the normal paragraph, named 'DBS-Normal', which uses a

−

# 12 point, Serif font.

+

# 12 point, Serif font.

−

font = docgen.FontStyle()

+

font = docgen.FontStyle()

−

font.set_size(12)

+

font.set_size(12)

−

font.set_type_face(docgen.FONT_SERIF)

+

font.set_type_face(docgen.FONT_SERIF)

−

para = docgen.ParagraphStyle()

+

para = docgen.ParagraphStyle()

−

para.set_font(font)

+

para.set_font(font)

−

para.set_description(_('The style used for normal text'))

+

para.set_description(_('The style used for normal text'))

−

default_style.add_style('DBS-Normal',para)

+

default_style.add_style('DBS-Normal',para)

</pre>

</pre>

Line 71:

Line 70:

===Report class===

===Report class===

The user's report class should inherit from the Report class contained within the Report module. The constructor should take three arguments (besides class instance itself, usually denoted by 'self' name):

The user's report class should inherit from the Report class contained within the Report module. The constructor should take three arguments (besides class instance itself, usually denoted by 'self' name):

−

* GRAMPS database instance

+

* Gramps database instance

* options class instance

* options class instance

* user class instance

* user class instance

The first is the database to work with. The second is the instance of the options class defined in the same report, see next section. The third is an instance of the User class, used for interaction with the user. Here's an example of a report class definition:

The first is the database to work with. The second is the instance of the options class defined in the same report, see next section. The third is an instance of the User class, used for interaction with the user. Here's an example of a report class definition:

<pre>

<pre>

−

from ReportBase import Report, ReportUtils, ReportOptions

+

from ReportBase import Report, ReportUtils, ReportOptions

−

class ReportClassName(Report):

+

class ReportClassName(Report):

−

def __init__(self, database, options_class, user):

+

def __init__(self, database, options_class, user):

−

Report.__init__(self, database, options_class, user)

+

Report.__init__(self, database, options_class, user)

</pre>

</pre>

The Report class's constructor will initialize several variables for the user based off the passed values. They are:

The Report class's constructor will initialize several variables for the user based off the passed values. They are:

Line 91:

Line 90:

Report class '''must''' provide a <tt>write_report()</tt> method. This method should dump the report's contents into the already opened document instance.

Report class '''must''' provide a <tt>write_report()</tt> method. This method should dump the report's contents into the already opened document instance.

<pre>

<pre>

−

def write_report(self):

+

def write_report(self):

−

self.doc.start_paragraph("ABC-Title")

+

self.doc.start_paragraph("ABC-Title")

−

self.doc.write_text(_("Some text"))

+

self.doc.write_text(_("Some text"))

−

self.doc.end_paragraph()

+

self.doc.end_paragraph()

</pre>

</pre>

The rest of the report class is pretty much up to the report writer. Depending on the goals and the scope of the report, there can be any amount of code involved. When the user generates the report in any mode, the class constructor will be run, and then the <tt>write_report()</tt> method will be called. So if you wrote that beautiful method listing something really important, make sure it is eventually called from within the <tt>write_report()</tt>. Otherwise nobody will see it unless looking at the code.

The rest of the report class is pretty much up to the report writer. Depending on the goals and the scope of the report, there can be any amount of code involved. When the user generates the report in any mode, the class constructor will be run, and then the <tt>write_report()</tt> method will be called. So if you wrote that beautiful method listing something really important, make sure it is eventually called from within the <tt>write_report()</tt>. Otherwise nobody will see it unless looking at the code.

Line 103:

Line 102:

====Using ReportOptions====

====Using ReportOptions====

<pre>

<pre>

−

class OptionsClassName(ReportOptions):

+

class OptionsClassName(ReportOptions):

−

def __init__(self,name,person_id=None):

+

def __init__(self,name,person_id=None):

−

ReportOptions.__init__(self,name,person_id)

+

ReportOptions.__init__(self,name,person_id)

</pre>

</pre>

* It should set new options that are specific for this report, by overriding the <tt>set_new_options()</tt> method which defines <tt>options_dict</tt> and <tt>options_help</tt> dictionaries:

* It should set new options that are specific for this report, by overriding the <tt>set_new_options()</tt> method which defines <tt>options_dict</tt> and <tt>options_help</tt> dictionaries:

<pre>

<pre>

−

def set_new_options(self):

+

def set_new_options(self):

−

# Options specific for this report

+

# Options specific for this report

−

self.options_dict = {

+

self.options_dict = {

−

'my_fist_option' : 0,

+

'my_fist_option' : 0,

−

'my_second_option' : '',

+

'my_second_option' : '',

−

}

+

}

−

self.options_help = {

+

self.options_help = {

−

'my_fist_option' : ("=num","Number of something",

+

'my_fist_option' : ("=num","Number of something",

−

[ "First value", "Second value" ],

+

[ "First value", "Second value" ],

−

True),

+

True),

−

'my_second_option' : ("=str","Some necessary string for the report",

+

'my_second_option' : ("=str","Some necessary string for the report",

−

"Whatever String You Wish"),

+

"Whatever String You Wish"),

−

}

+

}

</pre>

</pre>

−

* It should also enable the "semi-common" options that are used in this report, by overriding the <tt>enable_options</tt> method which defines <tt>enable_dict</tt> dictionary. The semi-commons are the options which GRAMPS knows about, but which are not necessarily present in all reports:

+

* It should also enable the "semi-common" options that are used in this report, by overriding the <tt>enable_options</tt> method which defines <tt>enable_dict</tt> dictionary. The semi-commons are the options which Gramps knows about, but which are not necessarily present in all reports:

<pre>

<pre>

−

def enable_options(self):

+

def enable_options(self):

−

# Semi-common options that should be enabled for this report

+

# Semi-common options that should be enabled for this report

−

self.enable_dict = {

+

self.enable_dict = {

−

'filter' : 0,

+

'filter' : 0,

−

}

+

}

</pre>

</pre>

−

All the common options are already taken care of by the core of GRAMPS.

+

All the common options are already taken care of by the core of Gramps.

* For any new options set up in the options class, there must be defined UI widgets to provide means of changing these options through the dialogs. Also, there must be defined methods to extract values of these options from the widgets and to set them into the class-variable dictionary:

* For any new options set up in the options class, there must be defined UI widgets to provide means of changing these options through the dialogs. Also, there must be defined methods to extract values of these options from the widgets and to set them into the class-variable dictionary:

Overview

Before going into details, it is useful to note that the report should have two basic parts. As explained on the Addons page, these go into two different files: the source code file (e.g.: report.py) and a Gramps Plugin Registration file (e.g.: report.gpr.py).

report.py

Report class

This is the code that takes data from the Gramps database and organizes it into the document structure. This structure can later be printed, viewed, or written into a file in a variety of formats. This class uses the docgen interface to abstract away the output format details.

Options class

This is the code that provides means to obtain options necessary for the report using a variety of available mechanisms.

report.gpr.py

Registration statement

This initializes the report by a single call to the register() function. It is trivial, but without it your report will not become available to Gramps, even if it is otherwise perfectly written.

A report can potentially be generated as a standalone report, as a Gramps Book item, and as a command line report. The registration determines which modes are enabled for a given report. The report class does not have to know anything about the mode. The options class is there to provide options interface for all available modes.

Document interface

Gramps attempts to abstract the output document format away from the report. By coding to the docgen class, the report can generate its output in the format desired by the end user. The document passed to the report (self.doc) could represent an HTML, OpenDocument, PDF or any of the other formats supported by the user. The report does not have to concern itself with the output format details, since all details are handled by the document object.

A document is composed of paragraphs, tables, and graphics objects. Tables and graphics objects will not be covered in this tutorial.

The report defines a set of paragraph and font styles, along with their default implementation. The user can override the definition of each style, allowing the user to customize the report. Each paragraph style must be named uniquely, to prevent collisions when printed in a book format. It is recommended to prefix each paragraph style with a three letter code unique to the report.

Paragraph and font styles are defined in the make_default_style() function of the options class. The paragraphs are grouped into a StyleSheet, which the make_default_style() function defines. For the example report (DbSummary), the paragraph styles are defined as below:

Defining the classes

Report class

The user's report class should inherit from the Report class contained within the Report module. The constructor should take three arguments (besides class instance itself, usually denoted by 'self' name):

Gramps database instance

options class instance

user class instance

The first is the database to work with. The second is the instance of the options class defined in the same report, see next section. The third is an instance of the User class, used for interaction with the user. Here's an example of a report class definition:

You'll probably need a start-person for which to write the report. This person should be obtained from the options_class object through the PersonOption class which will default to the active person in the database. Anything else the report class needs in order to produce the report should be obtained from the options_class object. For example, you may need to include the additional code in the report class constructor to obtain any options you defined for the report.

Report class must provide a write_report() method. This method should dump the report's contents into the already opened document instance.

The rest of the report class is pretty much up to the report writer. Depending on the goals and the scope of the report, there can be any amount of code involved. When the user generates the report in any mode, the class constructor will be run, and then the write_report() method will be called. So if you wrote that beautiful method listing something really important, make sure it is eventually called from within the write_report(). Otherwise nobody will see it unless looking at the code.

Options class

Options class should derive from ReportOptions class. Usually for a common report the MenuReportOptions class should be derived from. MenuReportOptions will abstract most of the lower-level widget handling below.

It should also enable the "semi-common" options that are used in this report, by overriding the enable_options method which defines enable_dict dictionary. The semi-commons are the options which Gramps knows about, but which are not necessarily present in all reports:

All the common options are already taken care of by the core of Gramps.

For any new options set up in the options class, there must be defined UI widgets to provide means of changing these options through the dialogs. Also, there must be defined methods to extract values of these options from the widgets and to set them into the class-variable dictionary:

Using MenuReportOptions

The MenuReportOptions can be used in place of ReportOptions to present the user with a standard interface for running the report. Instead of parsing options, you generate a menu using one or more of the classes available in gen.plug.menu. All these are initialzed in the add_menu_options function (which is a required function when you inherit from MenuReportOptions). For example:

In this example a BooleanListOption object is created that presents the user with a group of check boxes, one is created for each call to add_button. Finally the object is added to the menu with menu.add_option(). The category name is used to generate tabs on the report dialog.

Then to access the selected values once the user runs the report, you make a call the menu object from within the report's __init__ function. For example, to access the "what_types" that are selected from the menu above you would add the following code:

In the example, the option class is retrieved by the get_option_by_name() function. The string must match the name you passed as the second argument to menu.add_option() when you created the menu. Then a list of the selected item titles is retrieved with get_selected() and stored as a class member for later use.

Implementation

Defining the Report Options class

In this example, no special options are required. This makes the options class very simple. All that is necessary is to define the default styles.

Defining the Report class

The actual implemention of the DbSummary report is rather simple. No additional work needs to be done to initialize the class, so the parent __init__ routine is called.

All the work is done in the write_report() function. This function uses a GrampsCursor to iterate through the map of Person objects and gathers some simple statistics.

The only thing of any complication is the determination of the most common surname. A python dictionary is used to store the number of times each surname is used. Each time a surname is encountered, the value in the dictionary is incremented. The results are then loaded into a list and sorted, allowing us to find the most common name by looking at the last entry in the list.

Registering the report

Registration is set into a name.gpr.py file

Registration should define internal name of the report (preferably, single string with non-special ascii characters, usable for report identification from the command line and in the options storage, as well as for forming sane filename for storing its own styles). It should also define the report's...

category (text/graphics/code)

translated name (the one to display in menus)

modes that should be enabled for the report (standalone, book item, command line)

If the report requires an active person to run, then require_active should be set to True.

Finally, both report class and options class should be passed to registration.

The list should only include those options which are valid for this report.

The rest of the options should be self-explanatory.

Book Report

To turn a report into one that will work as a book report, you may need to add the following:

from gen.plug.docgen import IndexMark
# Write the title line.
# Set in INDEX marker so that this section will be identified
# as a major category if this is included in a Book report.
title = self._('My Report')
mark = IndexMark(title, INDEX_TYPE_TOC, 1)
self.doc.start_paragraph('MYREPORT-section')
self.doc.write_text(title, mark)
...