Hi,
Developing a tool (my first) to check a db, but cannot get the run method to execute except by explicitly calling it in the tool init method (which I am sure is not what’s intended, if I do that the tool does what I expect). I have looked at the Tool code but cannot figure it out. Also, I have an options class derived from MenuToolOptions and can see it getting initialized and its add_menu_options method being called, but am not getting a window for interaction. Thanks.
I’m not going to be much direct help but…
do you have a repository on GitHub with the code shared? Other volunteers will be able to hone in on an answer if they can peek at your code.
Not yet, will do that. Thanks
You could share your code here too if you want to do the GitHub account later.
Post the code snippet with 3 backticks on the lines above and below.
Adding python
after the initial backticks line would make this forum apply the right formatting:
Let’s see if this makes any sense to anybody. Thanks.
"""FamilySearch Check Tool"""
import os
import pdb
from typing import List, Optional, AnyStr, NoReturn
# from queue import Queue
import logging
import re
# from unicodedata import category
# from gi.repository import Gtk
import gramps.gen.lib
from gramps.gen.plug.menu import BooleanListOption
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gui.plug import tool, MenuToolOptions
#from gramps.gen.plug.menu import FilterOption, StringOption, BooleanOption
from gramps.gen.plug.menu import StringOption
from gramps.gui.managedwindow import ManagedWindow
# from gramps.gen.simple import SimpleAccess
from genealogy_familysearch import (FamilySearchPopulator, Missing,
FamilySearchPerson)
from genealogy_gramps import GrampsPopulator, GrampsPerson
# from po.update_po import merge
LOG = logging.getLogger("FSCheck")
LOG.setLevel(logging.DEBUG)
_ = glocale.translation.sgettext
class WorkItem:
"""Work Item to process"""
def __init__(self, gid: AnyStr, fsid: AnyStr, gen: int = 0):
"""Initializer"""
self._gid: str = gid
self._fsid: str = fsid
self._gen: int = gen
@property
def gid(self) -> str:
"""Gramps ID"""
return self._gid
@property
def fsid(self) -> str:
"""FamilySearch ID"""
return self._fsid
@property
def gen(self) -> int:
"""Generation of the work item"""
return self._gen
# class FamilySearchCheck(tool.Tool, ManagedWindow):
class FamilySearchCheck(tool.Tool, ManagedWindow):
"""Check a tree against FamilySearch"""
# pylint: disable=unused-argument
# pylint: disable=too-many-arguments,too-many-positional-arguments
def __init__(self, dbstate, user, options_class, name, callback=None):
LOG.debug("FamilySearchCheck init")
# self.window = None
# uistate = user.uistate
# This inheritance provides the db property
# This will instantiate options_class and set self.options to the
# result
tool.Tool.__init__(self, dbstate, options_class, name)
# tool.BatchTool.__init__(self, dbstate, user, options_class, name)
# self.window_name = _('FamilySearch Check Tool')
# ManagedWindow.__init__(self, user.uistate, [], self.__class__)
# self.set_window(Gtk.Window(), Gtk.Label(), self.window_name)
# We need a stack
self._todo: List = []
#self._inque: Set = set()
self._gens: int = 0
# We are only ever dealing with a single database
# self._sdb = dbstate
LOG.debug("Database: %s", self.db)
# The populators
self._gpop = None
self._fspop = None
# self.show()
# self.run()
LOG.debug("FamilySearchCheck init done")
class FamilySearchCheckOptions(MenuToolOptions):
"""Option class for event description editor."""
def __init__(self, name, person_id=None, dbstate=None):
# name is the id from the gpr
LOG.debug("FamilySearchCheckOptions init")
# add_menu_options is called here
MenuToolOptions.__init__(self, name, person_id, dbstate)
pdb.set_trace()
LOG.debug("FamilySearchCheckOptions init done")
def add_menu_options(self, menu):
"""Menu options."""
LOG.debug("Adding options")
# menu.filter_list = CustomFilters.get_filters("Event")
# all_filter = GenericFilterFactory("Event")()
# all_filter.set_name(_("All Events"))
# all_filter.add_rule(rules.event.AllEvents([]))
# all_filter_in_list = False
# for fltr in menu.filter_list:
# if fltr.get_name() == all_filter.get_name():
# all_filter_in_list = True
# if not all_filter_in_list:
# menu.filter_list.insert(0, all_filter)
#
# events = FilterOption(_("Events"), 0)
# menu.add_option(_("Option"), "events", events)
# events.set_filters(menu.filter_list)
#
#find = StringOption(_("Find"), "")
#menu.add_option(_("Option"), "find", find)
category_name = "Tool Options"
merge = BooleanListOption("Merge options")
merge.add_button("Always", False)
merge.add_button("Confirm", False)
merge.add_button("Never", True)
menu.add_option(category_name, "merge", merge)
# replace = StringOption(_("Replace"), "")
# menu.add_option(_("Option"), "replace", replace)
#
# keep_old = BooleanOption(_("Replace substring only"), False)
# keep_old.set_help(_("If True only the substring will be replaced, "
# "otherwise the whole description will be deleted "
# "and replaced by the new one."))
# menu.add_option(_("Option"), "keep_old", keep_old)
#
# regex = BooleanOption(_("Allow regex"), False)
# regex.set_help(_("Allow regular expressions."))
# menu.add_option(_("Option"), "regex", regex)
LOG.debug("Adding options done")
Committed to GitHub - lschopitea/gramps-addon-familysearch-check: Gramps addon to check person's ancestors against FamilySearch
FamilySearch, eh? Nice target!
You might want to collaborate with @jmichault
His https://raw.githubusercontent.com/jmichault/gramps-kromprogramoj/gramps52
repository can be added to your Addon Manager (see adding project to Addon Manager) He has already in beta with 2 tools and a gramplet related to to FamilySearch.
And the old repository of ElderEvans has a decade-old stagnant barebones experiment. But he’s the contact in the LDS technical organization.
The US Web Connect in searches for FamilySearch. But it keeps falling afoul of linkrot in login sysem and the search parameter passing.
Trivial things first…
set your addons Registration help_url
attribute to your GitHub repository while you are in development. People can communicate with you via GitHub Issues
help_url="https://github.com/lschopitea/gramps-addon-familysearch-check/tree/main/FamilySearchCheck",
Set the status=BETA
(or UNSTABLE or EXPERIMENTAL )
Got it. Set it to STABLE because at some point it stopped loading because it failed the check (probably something I have to set in my settings).
Which version of Gramps are you using? The gpr file says it is only for 6.0
The error message in the Gramps 6.0 GUI is not enlightening.
But the Console error message running the addon tool is better
2025-02-20 18:20:26.148: WARNING: _manager.py: line 326: Plugin error (from 'familysearchcheck'): No module named 'genealogy_familysearch'
There is no genealogy_familysearch
or genealogy_gramps
from which to import.
UNSTABLE status Plugins are supposed to only be available when you are running Gramps with the Debug/Developer mode switch
For sometime since 5.0 , that restriction became broken and the developer Gramps showed everything. I think that there was a fix in 6.0 and that would be a reason the addon would stop showing up.
But the registration doesn’t give a lot of feedback on a failure either.
(BTW, I am not a developer.)
Yes, I am running from a checkout of master.
Yes, I did not post those up. Here is my run log (partial):
2025-02-20 20:19:46.584: DEBUG: _manager.py: line 301: Importing familysearchcheck
2025-02-20 20:19:46.882: DEBUG: familysearchcheck.py: line 62: FamilySearchCheck init
2025-02-20 20:19:46.882: DEBUG: familysearchcheck.py: line 359: FamilySearchCheckOptions init
2025-02-20 20:19:46.882: DEBUG: familysearchcheck.py: line 367: Adding options
2025-02-20 20:19:46.883: DEBUG: familysearchcheck.py: line 405: Adding options done
/home/lsc/.gramps/gramps60/plugins/familysearchcheck.py(363)init()
→ LOG.debug(“FamilySearchCheckOptions init done”)
(Pdb) c
2025-02-20 20:19:52.401: DEBUG: familysearchcheck.py: line 363: FamilySearchCheckOptions init done
2025-02-20 20:19:52.402: DEBUG: familysearchcheck.py: line 81: Database: <sqlite.SQLite object at 0x75866fde0580>
2025-02-20 20:19:52.402: DEBUG: familysearchcheck.py: line 89: FamilySearchCheck init done
It goes through all the init code, but the run method never executes, and I don’t get any sor of options dialog.
The missing libraries are only exercised in the run() method (which I have tried as run() and run_tool()).
You may also note that I added some logging to the gramps libs to see what was going on.
Afraid that we’ve reached the limits of my awareness of the obvious stuff.
So this seems to be the relevant code that instantiates the tool object, notice that it does not execute the run() method:
def gui_tool(
dbstate, user, tool_class, options_class, translated_name, name, category, callback
):
"""
tool - task starts the report. The plugin system requires that the
task be in the format of task that takes a database and a person as
its arguments.
"""
try:
tool_class(
dbstate=dbstate,
user=user,
options_class=options_class,
name=name,
callback=callback,
)
except WindowActiveError:
pass
except:
log.error("Failed to start tool.", exc_info=True)
Called from here:
def run_plugin(self, pdata):
"""
run a plugin based on it's PluginData:
1/ load plugin.
2/ the report is run
"""
mod = self._pmgr.load_plugin(pdata)
if not mod:
# import of plugin failed
return
if pdata.ptype == REPORT:
report(
self.state,
self.uistate,
self.uistate.get_active("Person"),
getattr(mod, pdata.reportclass),
getattr(mod, pdata.optionclass),
pdata.name,
pdata.id,
pdata.category,
pdata.require_active,
)
else:
from ..user import User
tool.gui_tool(
dbstate=self.state,
user=User(uistate=self.uistate),
tool_class=getattr(mod, pdata.toolclass),
options_class=getattr(mod, pdata.optionclass),
translated_name=pdata.name,
name=pdata.id,
category=pdata.category,
callback=self.state.db.request_rebuild,
)
and again, no call to the run() method. So where, and under what conditions, is the run() method called?
In my experience you typically really have to call run()
explicitly in __init__
. And you must write the run
method yourself. Gramps just instantiates the Tool object and it must take care of everything else (like setting up the GUI, for example). This is a bit different than in e.g. gramplets. Of course, there also does not need to be a separate method - you can do everything in __init__
.
If your tool inherits from the ToolManagedWindow
or ToolManagedWindowBatch
class, then the run()
method is called for you.
When developing different types of Tools for Gramps, should the approach to triggering the run()
method vary based on the Tool’s behavior? Specifically, consider these three observed behavioral types of Tools:
- Immediate execution Tools: These run as soon as they’re selected from the menu, typically without an ellipsis (
…
U+2026 Horizontal Ellipsis Unicode Character) in their menu item name. These might have a “Undoable” warning dialog with a cancel option (although the cancel option means the menu could have the…
), progress dialog and/or ‘after action’ report. - Configurable Tools: These have options that need to be set before execution, after which the tool’s actions run once and are done.
- Interactive Tools: These allow for ongoing user interaction and iterative actions.
How would the implementation and triggering of the run()
method differ for each of these Tool types to ensure proper functionality and user experience within the Gramps environment?