Changes

Jump to: navigation, search

Report-writing tutorial

2,762 bytes added, 17:38, 11 December 2014
Clarify where to find additional information
{{Out of date}}
{{languages}}
[[Category:Developers/Tutorials]][[Category:Developers/General]]==Introduction==This tutorial covers the basics of writing a simple report using the GRAMPS 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 number of people in the database
* The number of males and females
* The number of unique surnames.
* The most common surname
As of Gramps version 3.02, there is also a [[Simple Access API|simple access]] database [http://www.gramps-project.org/docs/ API] is available, with accompanying [[Quick ReportsViews]], [[Gramplets]] and [[Addons development|Addons]].
==Overview==
Before going into details, it is useful to note that the report should have three 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 class : This is the code that takes data from the GRAMPS 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://wwwpackages.gramps-projectpython.org/docsGramps/gen/ BaseDocgen_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.
;Registration statement : This is a single call to <tt>register_report()</tt> function in the module. It is trivial, but without it your report will not become available to GRAMPS, even if it is otherwise perfectly written.
===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. A report can potentially be generated as a standalone report, as a GRAMPS 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 [[Report Generation]] provides an overview of the 'docgen' interfaces that are available for outputting documents. [[Report API]] provides more details about the interfaces. [http://packages.python.org/Gramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen] provides a detailed specification of the interfaces. Gramps attempts to abstract the output document format away from the report. By coding to the [http://wwwpackages.gramps-projectpython.org/docs BaseDocGramps/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.
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>
def make_default_style(self, default_style):
# Define the title paragraph, named 'DBS-Title', which uses a # 18 point, bold Sans Serif font with a paragraph that is centered
font = BaseDocdocgen.FontStyle() font.set_size(18) font.set_type_face(BaseDocdocgen.FONT_SANS_SERIF) font.set_bold(True)
para = BaseDocdocgen.ParagraphStyle() para.set_header_level(1) para.set_alignment(BaseDocdocgen.PARA_ALIGN_CENTER) para.set_font(font) para.set_description(_('The style used for the title of the page.'))
default_style.add_style('DBS-Title',para)
# Define the normal paragraph, named 'DBS-Normal', which uses a # 12 point, Serif font.
font = BaseDocdocgen.FontStyle() font.set_size(12) font.set_type_face(BaseDocdocgen.FONT_SERIF)
para = BaseDocdocgen.ParagraphStyle() para.set_font(font) para.set_description(_('The style used for normal text'))
default_style.add_style('DBS-Normal',para)
</pre>
===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 Gramps database instance* Person object instance
* options class instance
* user class instanceThe first is the database to work with. The second is the person on whom the report is centered. The third 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>
from ReportBase import Report, ReportUtils, ReportOptions
class ReportClassName(Report): def __init__(self,database,personoptions_class,options_classuser): Report.__init__(self,database,personoptions_class,options_classuser)
</pre>
The Report class's constructor will initialize several variables for the user based off the passed values. They are:
;self.doc : The opened document instance ready for output. This is of the type [http://wwwpackages.gramps-projectpython.org/docs BaseDocGramps/gen/gen_plug.html#module-gen.plug._docgenplugin docgen], and is '''not''' a normal file object.;self.start_person database : The [http://www.gramps-project.org/docs/gen/gen_lib.html#module-gen.lib.person PersonGrampsDbBase] instance containing the start or center person (the person selected) when the report was calleddatabase object.;self.database options_class : The [http://www.gramps-projectpythonhosted.org/docsGramps/gen/gen_libgen_plug.html#module-gen.lib GrampsDbBase] database object;selfplug.options_class : The [http://www.gramps-project.org/devdoc/api/2.2/private/ReportBase._ReportOptions.ReportOptions-class.html _docgenplugin ReportOptions] class passed to the reportAnything else the report class needs in order to produce the report should be obtained from the <tt>options_class</tt> 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.
You'll probably need a start-person for which to write the report. This person should be obtained from the <tt>options_class</tt> 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 <tt>options_class</tt> 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 <tt>write_report()</tt> method. This method should dump the report's contents into the already opened document instance.
<pre>
def write_report(self): self.doc.start_paragraph("ABC-Title") self.doc.write_text(_("Some text")) self.doc.end_paragraph()
</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.
===Options class===
* Options class should derive from [http://wwwpackages.gramps-projectpython.org/devdocGramps/apigen/2gen_plug.2/private/ReportBasehtml#module-gen._ReportOptionsplug._options ReportOptions] class. Usually for a common report the <tt>MenuReportOptions</tt> class should be derived from. <tt>MenuReportOptions</tt> will abstract most of the lower-classlevel widget handling below.html  ====Using ReportOptions] class:====
<pre>
class OptionsClassName(ReportOptions): def __init__(self,name,person_id=None): ReportOptions.__init__(self,name,person_id)
</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:
<pre>
def set_new_options(self): # Options specific for this report self.options_dict = { 'my_fist_option' : 0, 'my_second_option' : '', } self.options_help = { 'my_fist_option' : ("=num","Number of something", [ "First value", "Second value" ], True), 'my_second_option' : ("=str","Some necessary string for the report", "Whatever String You Wish"), }
</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 Gramps knows about, but which are not necessarily present in all reports:
<pre>
def enable_options(self): # Semi-common options that should be enabled for this report self.enable_dict = { 'filter' : 0, }
</pre>
All the common options are already taken care of by the core of GRAMPSGramps.
* 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:
<pre>
def add_user_options(self,dialog): option_menu = gtk.OptionMenu() self.the_menu = gtk.Menu()
for item_index in range(10): item = _("Item numer %d") % item_index menuitem = gtk.MenuItem(item) menuitem.show() self.the_menu.append(menuitem)
option_menu.set_menu(self.the_menu) option_menu.set_history(self.options_dict['my_first_option'])
dialog.add_option(_('My first option'),option_menu)
self.the_string_entry = gtk.Entry() if self.options_dict['my_second_option']: self.the_string_entry.set_text(self.options_dict['my_second_option']) else: self.the_string_entry.set_text(_("Empty string")) self.the_string_entry.show() dialog.add_option(_('My second option'),self.the_string_entry)
def parse_user_options(self,dialog): self.options_dict['my_second_option'] = unicode(self.the_string_entry.get_text()) self.options_dict['my_first_option'] = self.the_menu.get_history()
</pre>
* Finally, the default definitions for the user-adjustable paragraph styles must be defined here, to form a 'default' stylesheet:
<pre>
def make_default_style(self,default_style): f = BaseDocdocgen.FontStyle() f.set_size(10) f.set_type_face(BaseDocdocgen.FONT_SANS_SERIF) p = BaseDocdocgen.ParagraphStyle() p.set_font(f) p.set_description(_("The style used for the person's name.")) default_style.add_style("ABC-Name",p)
</pre>
===Registration statement=Using MenuReportOptions==== * Registration should define internal name The <tt>MenuReportOptions</tt> can be used in place of <tt>ReportOptions</tt> to present the report (preferably, single string user with non-special ascii characters, usable a standard interface for running the report identification from . Instead of parsing options, you generate a menu using one or more of the command line and classes available in the options storage, as well as for forming sane filename for storing its own styles)<tt>gen.plug. It should also define report's category (textmenu</graphics/code), translated name (the one to display tt>. All these are initialzed in menus), and the modes that should be enabled for the report <tt>add_menu_options</tt> function (standalone, book item, command linewhich is a required function when you inherit from <tt>MenuReportOptions</tt>). Finally, both report class and options class should be passed to registration. Here's the For example registration statement
<pre>
from PluginUtils import register_report register_reportdef add_menu_options( name = 'shortname'self,menu): category = CATEGORY_TEXT, """ report_class = ReportClassName, Add options to the menu for this report. options_class = OptionsClassName, """ modes = MODE_GUI | MODE_BKI | MODE_CLI, translated_name category_name = _("Totally new reportReport Options"), status what_types = BooleanListOption(_("Alpha"'Select From:')) what_types.add_button(_('People'),True) author_name = "A what_types. Uadd_button(_('Families'), False) what_types. Thor"add_button(_('Places'),False) author_email = "person@address what_types.net"add_button(_('Events'),False) description = _ menu.add_option(category_name, "Produces totally new reportwhat_types") , what_types)
</pre>
The first two arguments set string identifier and In this example a <tt>BooleanListOption</tt> object is created that presents the category (in this case we are considering text report)user with a group of check boxes, one is created for each call to <tt>add_button</tt>. The next two pass Finally the object is added to the report class and options class. The menu with <tt>modesmenu.add_option()</tt> argument . The category name is set used to generate tabs on the bit-wise sum (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 <tt>OR__init__</tt> statementfunction. For example, to access the "what_types" that are selected from the menu above you would add the following code: <pre>class ExampleReport(Report) of all three possible modes: GUI   def __init__(standalone report generated from GRAMPS running in a windowself, database, options_class):  Report.__init__(self, BKI database, options_class)  menu_option = options_class.menu.get_option_by_name('what_types') self.what_types = menu_option.get_selected(book item)</pre> In the example, and CLI the option class is retrieved by the <tt>get_option_by_name(command line interface)</tt> function. This means that The string must match the name you passed as the second argument to <tt>menu.add_option()</tt> when you created the report will be available in all three modesmenu. The rest should be self-explanatory Then a list of the selected item titles is retrieved with <tt>get_selected()</tt> and stored as a class member for later use.
==Implementation==
class DbSummaryOptions(ReportOptions):
def __init__(self, name, person_id=Nonedatabase):
ReportOptions.__init__(self, name, person_iddatabase)
def make_default_style(self, default_style):
# 18 point, bold Sans Serif font with a paragraph that is centered
font = BaseDocdocgen.FontStyle()
font.set_size(18)
font.set_type_face(BaseDocdocgen.FONT_SANS_SERIF)
font.set_bold(True)
para = BaseDocdocgen.ParagraphStyle()
para.set_header_level(1)
para.set_alignment(BaseDocdocgen.PARA_ALIGN_CENTER)
para.set_font(font)
para.set_description(_('The style used for the title of the page.'))
# 12 point, Serif font.
font = BaseDocdocgen.FontStyle()
font.set_size(12)
font.set_type_face(BaseDocdocgen.FONT_SERIF)
para = BaseDocdocgen.ParagraphStyle()
para.set_font(font)
para.set_description(_('The style used for normal text'))
class DbSummaryReport(Report):
def __init__(self, database, person, options_class):
Report.__init__(self, database, person, options_class)
def write_report(self):
</pre>
===Registering the Reportreport===The * 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 must '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 registered before GRAMPS can find itset to True.* Finally, both report class and options class should be passed to registration. Here's the example registration statement:
<pre>
from PluginUtils import register_reportregister(REPORT, id = 'an unique id', name = _("Name of the plugin"), description = _("Produces a ..."), version = '1.0', gramps_target_version = '3.2', status = STABLE, fname = 'filename.py', authors = ["John Doe"], authors_email = ["[email protected]"], category = CATEGORY_TEXT, require_active = False, reportclass = 'DbSummaryReport', optionclass = 'DbSummaryOptions', report_modes = [REPORT_MODE_GUI, REPORT_MODE_CLI],)</pre>There are arguments for the report class and options class. The <tt>report_modes</tt> argument is set to a list of zero or more of the following modes:
register_report* REPORT_MODE_GUI (available for Gramps running in a window, graphical user interface) name * REPORT_MODE_BKI (available as a book report item)* REPORT_MODE_CLI (available for the command line interface) The list should only include those options which are valid for this report. The rest of the options should be self-explanatory. ====Book Report= 'database_summary'=== To turn a report into one that will work as a book report,you may need to add the following: <pre> category = constfrom gen.plug.docgen import IndexMark # Write the title line.CATEGORY_TEXT, report_class = DbSummaryReport,# Set in INDEX marker so that this section will be identified options_class = DbSummaryOptions,# as a major category if this is included in a Book report. modes = MODE_GUI, translated_name title = self._('Database Summary My Report'), statusmark =_IndexMark('Beta'title, INDEX_TYPE_TOC, 1), description=_self.doc.start_paragraph('Generates a summary of the databaseMYREPORT-section'), author_name="put author's name here"self.doc.write_text(title,mark) author_email="put email address here" )...
</pre>
A complete copy of the See an existing book report can be for more details. ==See also== * [[Report_API|Report API]]* [[mediaReport_Generation|Report Generation]] [[Category:DbSummary.zip|downloaded (1.66 KB)Addons]][[Category:Developers/General]][[Category:Developers/Tutorials]][[Category: Plugins]][[Category:Reports]] for testing and experimentation.
manual
389
edits

Navigation menu