Filter working on Descendant chart report

Gramps version 6

Is it possible to add a filter which works on the standard descendant chart report?

I would like to make a descendant chart report, which includes both male and female descendants, but stops at the female descendants, because they change their names due to marriage.

So the report should show, how the surname of an ancestor is transfered through generations.

What if the female descendant doesn’t change her name? Would her descendants be included?

If you can give some further examples, that would help.

Adding a filter sounds like it would have multiple uses, not just a surname inheritance chart. You might add a primogeniture filter. Or a lines of descent for cousins to common ancestor.

There have been other threads where a Report was forked (as an addon) and a filtering option was added. That would probably be a way ‘forward’ here.

However, you would do well to explore the FamilyTreeView addon by @ztlxltl. It allows such filtering to be applied to who will be shown in a chart. So all that would be necessary is designing the filter.

Well, let’s suppose in traditional, (oldfashioned?) way, females always take the surname of their spouse. I would like to display, that a familyname is transfered this way. At least in my usecase, an option to choose exactly this common case, would be helpful.

Can you link to these threads, so that I can try to follow them and also test the familiytree addon?

Here’s is a workflow suggested by Perplexity AI with the prompt:
Review the Report Writing tutorial developer wiki article about creating a Report addon plugin for Gramps genealogical software. Please create a simplified workflow for forking an existing Report with no filtering options (built-in or add-on) and its .gpr.py registration file, then adding a Person filtering option to it.

Perplexity responded:

Here is a simplified workflow for forking an existing Gramps Report addon that currently has no filtering options, then adding a Person filter to it. The tutorial shows that a report addon is split into two files: the main report.py and the registration file report.gpr.py, and that report options are added through an options class using menu options such as PersonOption and MenuReportOptions.

Workflow

  1. Copy the existing report addon into a new plugin directory.

    • Duplicate both report.py and report.gpr.py so your new report starts as a clean fork of the original. The tutorial explicitly says the source goes into these two files, and when installing manually both files should be copied into the addon directory.
  2. Rename the addon internals.

    • Change the report class name, options class name, plugin id, and filenames so they are unique and do not collide with the original addon. The registration section says the addon needs a unique internal id, plus matching reportclass and optionclass names.
  3. Add a Person option in the options class.

    • In report.py, extend the report’s options class and override addmenuoptions.
    • Add a PersonOption named something like person, so the user can choose the person the report should start from.
    • The tutorial notes that a report “will probably need a start-person,” and that this should be obtained through PersonOption, which defaults to the active person.
  4. Read the selected person in the report class.

    • In the report class __init__, retrieve the option from optionsclass.menu.getoptionbyname(...).
    • Store the chosen person in an instance variable, then use it later in writereport or helper methods. The tutorial shows the general pattern of fetching options from the menu in the report constructor and keeping them for later use.
  5. Update the report logic to use the filter.

    • Replace “iterate over everyone” logic with logic that starts from the selected person.
    • Depending on the report, this may mean limiting the report to that person, their family, descendants, ancestors, or a related subset. The tutorial’s core point is that the report class should use the options object to control what data is collected.
  6. Keep or add localization and style options only if needed.

    • The tutorial includes standard localization and optional paragraph-style setup, but those are separate from the filtering change. If the original report has no options, you only need to add the new Person filter and any minimum supporting options.
  7. Update report.gpr.py.

    • Make sure the registration file points to the renamed report.py, the new report class, and the new options class.
    • Keep requireactive set appropriately for your design; if the report depends on a chosen person, requireactive=True is often consistent with that behavior. The tutorial explains that registration controls the mode, id, and whether an active person is required.

Minimal implementation pattern

A typical implementation pattern looks like this:

  • report.py

    • class MyReportOptions(MenuReportOptions):
      • define addmenuoptions
      • add PersonOption(...)
    • class MyReport(Report):
      • in __init__, read the selected person option
      • in writereport, generate output using that person
  • report.gpr.py

    • register the addon
    • set reportclass=MyReport
    • set optionclass=MyReportOptions
    • update id, name, description, and fnamemodule

Practical fork order

If you want the safest sequence, do it in this order:

  1. Copy the addon.
  2. Rename the plugin metadata and class names.
  3. Add PersonOption to the options UI.
  4. Read the selected person in __init__.
  5. Change the report logic to honor that person.
  6. Test the addon in Gramps.
  7. Adjust the registration metadata if Gramps does not list the addon correctly.

same prompt to Claude AI

I’ll read the PDF content you’ve shared and create a clear, practical workflow for forking a Gramps report and adding a Person filter option.

Here’s the simplified workflow:


Workflow: Fork a Gramps Report & Add a Person Filter

Step 1 — Copy the Existing Report Files

~/.local/share/gramps/gramps60/plugins/
└── MyFork/
    ├── myfork.py          # copied from original report.py
    └── myfork.gpr.py      # copied from original report.gpr.py

Never edit the originals. Work only in your fork directory.


Step 2 — Update myfork.gpr.py

Change only the fields that identify the plugin. Leave reportclass, optionsclass, and category matching your new class names.

# myfork.gpr.py
from gramps.gen.plug._pluginreg import *
from gramps.gen.const import GRAMPS_LOCALE as glocale

_ = glocale.translation.gettext

register(
    REPORT,
    id="MyForkReport",                    # unique ID — no spaces
    name=_("My Forked Report"),
    description=_("Fork of OriginalReport with person filter"),
    version="1.0",
    gramps_target_version="6.0",
    status=STABLE,
    fname="myfork.py",                    # must match your .py filename
    authors=["Your Name"],
    authors_email=["you@example.com"],
    category=CATEGORY_TEXT,
    require_active=False,                 # set True if filter needs active person
    reportclass="MyForkReport",           # must match class name in myfork.py
    optionclass="MyForkOptions",          # must match class name in myfork.py
    report_modes=[REPORT_MODE_GUI, REPORT_MODE_CLI],
)

Step 3 — Update myfork.py — Imports

Add the imports needed for person filtering. The key additions are PersonOption and stdoptions.

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2025  Your Name
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

# ------------------------
# Python modules
# ------------------------
import logging

# ------------------------
# Gramps modules
# ------------------------
from gramps.gen.plug.report import Report, MenuReportOptions, stdoptions
from gramps.gen.plug.menu import PersonOption
from gramps.gen.plug import docgen
from gramps.gen.const import GRAMPS_LOCALE as glocale

# ------------------------
# Gramps specific
# ------------------------
LOG = logging.getLogger(__name__)
_ = glocale.translation.gettext

Step 4 — Update the Options Class

Rename the class and add add_menu_options() with a PersonOption. If the original had no options class at all, create one from scratch inheriting MenuReportOptions.

#------------------------------------------------------------
#
# MyForkOptions
#
#------------------------------------------------------------
class MyForkOptions(MenuReportOptions):
    """Options for MyForkReport."""

    def add_menu_options(self, menu: object) -> None:
        """
        Add options to the menu for this report.

        :param menu: The menu object to add options to.
        """
        category_name = _("Report Options")

        # Person filter — defaults to the active person in the database
        pid = PersonOption(_("Filter Person"))
        pid.set_help(_("The person the report is run for"))
        menu.add_option(category_name, "pid", pid)

        # Standard locale option
        stdoptions.add_localization_option(menu, category_name)

    def make_default_style(self, default_style: object) -> None:
        """
        Define default paragraph styles.

        :param default_style: The default StyleSheet to populate.
        """
        # Title style
        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_style.add_paragraph_style("MFR-Title", para)

        # Normal style
        font = docgen.FontStyle()
        font.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"))
        default_style.add_paragraph_style("MFR-Normal", para)

Style prefix rule: Change the 3-letter prefix (here MFR-) to something unique to your report to avoid collisions in Book report mode.


Step 5 — Update the Report Class

Rename the class, read the pid option in __init__, look up the Person object, then use it in write_report().

#------------------------------------------------------------
#
# MyForkReport
#
#------------------------------------------------------------
class MyForkReport(Report):
    """Forked report with a person filter option."""

    def __init__(
        self,
        database: object,
        options_class: MyForkOptions,
        user: object,
    ) -> None:
        """
        Initialize the report.

        :param database: The Gramps database instance.
        :param options_class: The options class instance for this report.
        :param user: The User instance for interaction.
        """
        Report.__init__(self, database, options_class, user)

        # Read the person handle chosen in the options dialog
        pid_option = options_class.menu.get_option_by_name("pid")
        pid_value = pid_option.get_value()                    # returns a handle string
        self.person = self.database.get_person_from_gramps_id(pid_value)

        # Set report locale
        self.set_locale(
            options_class.menu.get_option_by_name("trans").get_value()
        )

    def write_report(self) -> None:
        """Write the report content to the document."""
        if self.person is None:
            LOG.warning("No person selected for report.")
            return

        name = self.person.get_primary_name().get_regular_name()

        self.doc.start_paragraph("MFR-Title")
        self.doc.write_text(self._("My Forked Report"))
        self.doc.end_paragraph()

        self.doc.start_paragraph("MFR-Normal")
        self.doc.write_text(self._("Report for: %s") % name)
        self.doc.end_paragraph()

        # --- your original report logic goes here,
        #     scoped to self.person as needed ---

Step 6 — Install & Test

# Restart Gramps to pick up the new plugin, or use the Plugin Manager
# to reload without restarting (Tools > Plugin Manager > Reload)

# Command-line smoke test (no GUI needed):
gramps -O "YourFamilyTree" \
       -a report \
       -p name=MyForkReport,pid=I0001

Quick Reference — Key Differences from the Original

What changed Original Fork
File names report.py / report.gpr.py myfork.py / myfork.gpr.py
Plugin id original ID new unique string
Class names original names MyForkReport / MyForkOptions
Style prefix original (e.g. DBS-) new prefix (e.g. MFR-)
Options class base none or MenuReportOptions MenuReportOptions
Person option absent PersonOption("pid") added
require_active depends set True if person must be active

Commit Message Template (per Gramps AI guidelines)

Add person filter option to forked DbSummary report.

Fork of the built-in Database Summary report. Adds a PersonOption
so the report can be scoped to a single selected individual.

Generated-by: Claude Sonnet 4.6, Anthropic, claude-sonnet-4-6
Prompts: "Create a simplified workflow for forking an existing Gramps
report with no filtering options and adding a PersonOption to it,
following AGENTS.md and the report-writing tutorial."

Co-authored-by: Claude Sonnet 4.6, Anthropic <noreply@anthropic.com>