==Defining the classes==
===Report class===
The user's report class should inherit from the [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._reportbase.Report Report] class contained within the <code>gramps.gen.plug.report</code> module. The constructor should take three arguments (besides class instance itself, usually denoted by 'self' name):
* Gramps database instance
* options class instance
;self.options_class : The [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.ReportOptions ReportOptions] class passed to 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 [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._person.PersonOption 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.
===Options class===
* In theory Options class should derive from [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.ReportOptions ReportOptions] class. Usually But in pratice for a common report the <tt>[https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.MenuReportOptions MenuReportOptions</tt> ] class is used instead, which will abstract most of the lower-level widget handling. In this tutorial we'll assume that Options class should be is derived from[https://www.gramps-project. org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.MenuReportOptions MenuReportOptions]. ====Defining parameters of the report ==== To define options for your report, you need to override <ttcode>MenuReportOptions.add_menu_options()</ttcode> method. Then [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.MenuReportOptions MenuReportOptions] class will abstract most present the user with a standard menu interface for running the report. You may generate a menu using one or more of the lowerclasses available in [https://www.gramps-project.org/docs/gen/gen_plug.html#module-gramps.gen.plug.menu._menu <code>gramps.gen.plug.menu</code>] package such as [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption BooleanListOption] class. Also you may use standard Gramps options from [https://github.com/gramps-level widget handling belowproject/gramps/blob/dba1e4e5555694194836d2934542e30c0f75d1b6/gramps/gen/plug/report/stdoptions.py <code>gramps.gen.plug.report.stdoptions</code>] module.For example:
====Using ReportOptions====
<pre>
class OptionsClassNamedef add_menu_options(ReportOptionsself, menu): def __init__ """ Add options to the menu for this report. """ category_name = _("Report Options") what_types = BooleanListOption(_('Report for')) what_types.add_button(_('Males'), True) what_types.add_button(_('Females'), True) what_types.add_button(_(self'Unknown gender'),nameTrue) what_types.add_button(_('Other'),person_id=NoneTrue): ReportOptionsmenu.__init__add_option(selfcategory_name, "what_types",namewhat_types) stdoptions.add_localization_option(menu,person_idcategory_name)
</pre>
* It should set new options that are specific for In this example <code>category name</code> is used to generate tab "Report Options" on the reportparameter entry dialog. Object <code>what_types</code> of class [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption BooleanListOption] is created that presents the user with a group of check boxes, by overriding one is created for each call to [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption.add_button what_types.add_button()]. Finally the object is added to the menu with [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.add_option menu.add_option()]. Function <ttcode>set_new_optionsstdoptions.add_localization_option()</ttcode> method which defines adds to the same <ttcode>options_dictmenu</ttcode> and a standard Gramps option for localizing the report into a different locale from the UI locale. Then to access the selected values once the user runs the report, you make a call the menu object from within the report's <ttcode>options_help__init__()</ttcode> dictionariesmethod. For example, to access the "what_types" that are selected from the menu above you would add the following code:
<pre>
def set_new_options__init__(self, database, options_class, user): # Options specific for this report Report.__init__(self.options_dict = {, database, options_class, user) what_types_option = options_class.menu.get_option_by_name('my_first_optionwhat_types' : 0,) 'my_second_option' : '', } self.options_help what_types = {what_types_option.get_selected() 'my_first_option' : self.set_locale("=num","Number of something", [ options_class.menu.get_option_by_name("First valuetrans", "Second value" ], True), 'my_second_option' : .get_value("=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 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 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:
<pre>
def add_user_options(self,dialog):
option_menu = gtk.OptionMenu()
self.the_menu = gtk.Menu()
for item_index in rangeIn the example, <code>what_types_option</code> instance is retrieved by the [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.get_option_by_name options_class.menu.get_option_by_name(10)] method. The string passed to the method must match the name you passed as the second argument to [https: item = _//gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.add_option menu.add_option("Item numer %d") % item_index menuitem = gtk] when you created the menu.MenuItem( Then a list of the selected item) menuitemtitles is retrieved with [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption.get_selected what_types_option.showget_selected() ] and stored as a instance variable <code>self.the_menuwhat_types</code> for later use.append(menuitem)
option_menuThe standard Gramps option for localizing the report has name "trans".set_menuIts value is passed to [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._reportbase.Report.set_locale <code>Report.set_locale()</code>] method that creates <code>self.the_menu_() option_menu</code> method for the locale chosen by the report's user.set_historyIt is then used by <code>write_report()</code> (selfsee below).options_dict['my_first_option'])
dialog.add_option(_('My first option'),option_menu)====Defining user-adjustable paragraph styles ====
selfIf the report uses the user-adjustable paragraph styles the default definitions for the styles must be defined here by overriding method [https://www.the_string_entry = gtkgramps-project.Entry() if selforg/docs/gen/gen_plug.options_dict['my_second_option']: selfhtml#gramps.the_string_entrygen.set_text(selfplug.options_dict['my_second_option']) else: selfreport.the_string_entry_options.set_text(_("Empty string")) self.the_string_entryReportOptions.showmake_default_style make_default_style() dialog.add_option(_(], to form a 'My second optiondefault'),self.the_string_entry)style sheet:
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):
default_style.add_style("ABC-Name",p)
</pre>
====Using MenuReportOptions====
The [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.MenuReportOptions MenuReportOptions] can be used in place of [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.ReportOptions 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 [https://www.gramps-project.org/docs/gen/gen_plug.html#module-gramps.gen.plug.menu._menu Menu]. All these are initialized in the [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug._options.MenuOptions.add_menu_options add_menu_options()] function (which is a required function when you inherit from [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.report._options.MenuReportOptions MenuReportOptions]). For example:
<pre>
def add_menu_options(self, menu):
"""
Add options to the menu for this report.
"""
category_name = _("Report Options")
what_types = BooleanListOption(_('Select From:'))
what_types.add_button(_('People'), True)
what_types.add_button(_('Families'), False)
what_types.add_button(_('Places'), False)
what_types.add_button(_('Events'), False)
menu.add_option(category_name, "what_types", what_types)
</pre>
In this example a [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption BooleanListOption] object is created that presents the user with a group of check boxes, one is created for each call to [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption.add_button add_button()]. Finally the object is added to the menu with [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.add_option 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 <tt>__init__</tt> function. For example, to access the "what_types" that are selected from the menu above you would add the following code:
<pre>
class ExampleReport(Report):
def __init__(self, database, options_class):
Report.__init__(self, database, options_class)
menu_option = options_class.menu.get_option_by_name('what_types')
self.what_types = menu_option.get_selected()
</pre>
In the example, the option class is retrieved by the [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.get_option_by_name get_option_by_name()] function. The string must match the name you passed as the second argument to [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._menu.Menu.add_option menu.add_option()] when you created the menu. Then a list of the selected item titles is retrieved with [https://gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug.menu._booleanlist.BooleanListOption.get_selected get_selected()] and stored as a class member for later use.
==Implementation==
===Defining the Report Options ReportOptions 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. <pre>from gramps.gen.plug.report import Reportfrom gramps.gen.plug.report import ReportOptionsfrom gramps.gen.lib import Personfrom gramps.gen.plug import docgenfrom gramps.gen.const import GRAMPS_LOCALE as glocale_ = glocale.translation.gettext class DbSummaryOptions(ReportOptions): def __init__(self, name, database): ReportOptions.__init__(self, name, database) 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 = docgen.FontStyle() font.set_size(18) font.set_type_face(docgen.FONT_SANS_SERIF) font.set_bold(True) para = docgen.ParagraphStyle() para.set_header_level(1) para.set_alignment(docgen.PARA_ALIGN_CENTER) para.set_font(font) para.set_description(_('The style used for the title of the page.'))
default_styleIn this tutorial, the report has two options.add_paragraph_styleThe first, called <code>what_types</code>, allows a user to choose whether the counts of persons whose gender is Male, Female, Unknown or Other are shown in the report.The second is the standard Gramps option that allows the user to choose the locale for the report different from UI locale. It is added by calling <code>stdoptions.add_localization_option()</code> method. These two options are added in the overridden <code>add_menu_options('DBS-Title', para)</code> method.
# Define To define two unique styles for the normal paragraph, named 'report: DBS-Title and DBS-Normal', which uses a # 12 point, Serif font. font = docgen.FontStylethe method <code>make_default_style() font</code> is overridden too.set_size(12) font.set_type_face(docgen.FONT_SERIF) para = docgen.ParagraphStyle() para.set_font(font) para.set_description(_('The style used for normal text')) Both styles are added by calling <code>default_style.add_paragraph_style('DBS-Normal', para) </precode>method.
{{RWT_ReportOptions.py}}
So create now file '''report.py''' and copy there the above code.
===Defining the Report class===
The actual implemention implementation of the <ttcode>DbSummaryDbSummaryReport</ttcode> report is rather simple. No additional work needs to be done to To initialize the class, so the parent <ttcode>Report.__init__()</ttcode> routine is called. All and then values of the work is done in options entered by the user are extracted from <ttcode>write_report()options_class</ttcode> function. This function uses a parameter which is of class <ttcode>GrampsCursorDbSummaryOptions</ttcode> to iterate through the map of . An instance variable <ttcode>Personself.what_types</ttcode> objects and gathers some simple statistics. The only thing of any complication is the determination assigned a list of the most common surname. A python dictionary is used to store the number names 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 check boxes selected by looking at the last entry in the listuser.Then <precode>class DbSummaryReport(Report): def __init__(self, database, options_class, user): Report.__init__set_locale(self, database, options_class, user) def write_report(self): males = 0 females = 0 total = 0 surname_map = {} for person in self.database.iter_people(): if person.get_gender() == Person.MALE: males += 1 if person.get_gender() == Person.FEMALE: females += 1 total += 1 surname = person</code> method is called with the value of the locale to be used to run the report.get_primary_name().get_surname() if surname in surname_map: surname_map[surname] += 1 else: surname_map[surname] = 1 slist = [] for key in surname_map.keys(): slist.append((surname_map[key], key)) slist.sort()
selfAll the work is done in the <code>_count()</code> method.docIt uses a [https://www.start_paragraph("DBSgramps-Title") project.org/docs/gen/gen_db.html#gramps.gen.db.generic.DbGeneric.iter_people <code>self.docdatabase.write_textiter_people(_("Database Summary")) self</code>] to iterate through [https://www.gramps-project.org/docs/gen/gen_lib.html#module-gramps.gen.lib.person <code>Person</code>] objects and gathers some simple statistics. The only thing of any complication is the determination of the most common surnames. 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.docThe results are then loaded into a list and sorted in the reverse order <code>slist.end_paragraphsort(reverse=True)</code>, allowing us to find the most common names by looking at the first entries in the list.
self.doc.start_paragraphFinally <code>write_report('DBS-Normal') self</code> method outputs the data collected in accordance with the options selected by the report user.doc.write_textNote that the strings being used in the report (_('Number of males : %d') % malessuch as "Report Options", "Males" etc) selfhave already been used somewhere else in Gramps and have already been translated into various languages.doc.end_paragraph()Thus the content of our report as well as its parameters are already translated!
self.doc.start_paragraph('DBS-Normal')
self.doc.write_text(_('Number of females : %d') % females)
self.doc.end_paragraph()
self{{RWT_Report.doc.start_paragraph('DBS-Normal') self.doc.write_text(_('Total people : %d') % total) self.doc.end_paragraph() self.doc.start_paragraph('DBS-Normal') self.doc.write_text(_('Number of unique surnames : %d') % len(slist)) self.doc.end_paragraph() self.doc.start_paragraph('DBS-Normal') self.doc.write_text(_('Most common surname : %s') % (slist[-1][1])) self.doc.end_paragraph() </pre>py}}
Append the above code to the '''report.py''' file
===Registering the report===
* Registration is set into a ''<name>.gpr.py'' file
* The registration consists from a single call to [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug._pluginreg.register register()] function* Registration should define internal name <code>id</code> 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...** <code>category </code> (text/graphics/code)** translated translatable <code>name </code> (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 <code>require_active</code> should be set to <code>True</code>.** The <code>report_modes</code> argument is set to a list of zero or more of the following modes: *** REPORT_MODE_GUI (available for Gramps running in a window, graphical user interface)*** REPORT_MODE_BKI (available as a book report item)*** REPORT_MODE_CLI (available for the command line interface)** Finally, both report class <code>reportclass</code> and options class <code>optionsclass</code> names should be passed to registration. Descriptions of the other arguments of [https://www.gramps-project.org/docs/gen/gen_plug.html#gramps.gen.plug._pluginreg.register register()] function can be found [https://www.gramps-project.org/docs/gen/gen_plug.html#module-gramps.gen.plug._pluginreg here]. Here's the [[Quick_Views#Analysis:_registering_the_report| registration]] statement for our report:
<pre>
from gramps.gen.plug._pluginreg import registerfrom gramps.gen.plug._pluginreg import REPORT, STABLE, CATEGORY_TEXTfrom gramps.gen.plug._pluginreg import REPORT_MODE_GUI, REPORT_MODE_CLI*
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
)
</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:
* REPORT_MODE_GUI (available for Gramps running in a window, graphical user interface)
* 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.
Copy the above code into '''report.gpr.py''' file.
====Book ReportRemark regarding book report mode ====
To turn a report into one that will work as a book report, you may need add REPORT_MODE_BKI to the list of <code>report_modes</code> above. Also you have to add additional code to <code>write_report()</code> method of the Report class, similar to the followingcode below:
<pre>
</pre>
See an existing book report reports for more details. DO NOT copy the above code into '''report.py''' file!since we do not intend to use our report in Book mode.
== Manually installing your report ==