Skip to content

Sync addons-source@maintenance/gramps60 with upstream (2026-05-27) - #21

Merged
eduralph merged 31 commits into
maintenance/gramps60from
sync/upstream-maintenance-gramps60-auto
May 29, 2026
Merged

Sync addons-source@maintenance/gramps60 with upstream (2026-05-27)#21
eduralph merged 31 commits into
maintenance/gramps60from
sync/upstream-maintenance-gramps60-auto

Conversation

@eduralph

Copy link
Copy Markdown
Owner

Automated nightly sync from gramps-project/addons-source@maintenance/gramps60. Generated by .github/workflows/upstream-sync.yml on the testbed.

eduralph and others added 30 commits May 26, 2026 09:04
`RebuildTypes/RebuildTypes.gpr.py` declares
`include_in_listing=False`, `status=UNSTABLE`, `category=TOOL_DBFIX`
and `authors=["Nick Hall"]` - it is not published to users and is
flagged as a database-modifying tool that may corrupt data. The
addon also carried two stale Gramps-3 era pre-namespace imports
(`from gui.plug import tool`, `from QuestionDialog import OkDialog`)
that broke plugin registration on Python 3, so a load-time fix
would have been needed to revive it.

On PR gramps-project#877
(closed 2026-05-15, the prior port attempt) the addon's author and
the project maintainer agreed to drop it rather than port:

  Nick-Hall: "The RebuildTypes addon was either just an example or
  written for a particular person to fix a bug. An export followed
  by an import will achieve the same result. The Type Cleanup tool
  may also be a better option. I am happy for this addon to be
  deleted."

  GaryGriffin: "Seeing TOOL_DBFIX and UNSTABLE together is scary
  for a released addon."

Replace the dead/dangerous addon with its absence. Users needing
the same outcome have two supported alternatives Nick named:

  - export / re-import (rebuilds the type catalogue as a side
    effect of the round-trip)
  - the Type Cleanup tool (which is published and maintained)

No listing impact - the addon was already `include_in_listing=False`,
so `addons/gramps*/listings/addons-*.json` did not reference it; no
other tree references it either (`grep -rn RebuildTypes` on
`upstream/maintenance/gramps60` returns hits only inside the
`RebuildTypes/` directory itself).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The four ngettext() counter calls embedded a trailing \n in the plural
form but not the singular. gettext requires a msgid and its msgid_plural
to agree on the trailing newline, so msgfmt rejected every catalog that
translated these strings and `make.py build lxml` aborted with fatal
errors (Mantis bug 14234).

Append the layout newline in code instead -- as the pre-39fbcdd version
did -- so it is no longer part of the translatable string. The singular
case (count == 1) still gets its line break. The per-language catalogs
are not touched here: make.py extract-po regenerates them from the
corrected template, so the stale \n drops out of de/hr/nl/pt_PT/sk
through the normal Weblate pipeline rather than by hand-editing build
output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Provides nine filter rules (Person, Family, Event, Place, Source,
Citation, Repository, Media, Note) that match objects whose tag
names contain a given substring (case-insensitive).  Uses the
selected_handles optimizer pattern with find_backlink_handles so
only objects of the target namespace are fetched from the database.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closing a family tree emits 'database-changed'; change_db() clears the
history and calls redraw(), which -- finding no active person -- calls
change_object(None). That returned immediately, before the code that
rebuilds header/stack, so the view kept showing the closed tree's
people. Their edit buttons still held handles from the closed database,
and clicking one raised gramps.gen.errors.HandleError inside
get_person_from_handle() (Mantis bug 12572; the underlying stale-view
symptom is Mantis bug 14226).

On the no-active-object path, clear header/stack and disable the
per-page actions instead of returning early, mirroring the built-in
RelationshipView whose change_db() clears its container directly.
edit_active() is guarded against the now-possible active_page is None.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…10512)

## Root cause

The Sandclock Genealogy Tree report rendered only one page of an
arbitrarily-large tree.  The LaTeX `genealogytree` package's default
`database` template lays the tree out so widely that big sandclocks
clip off the page; the package itself does not support multi-page
output for that layout (confirmed by SNoiraud in note ~0046612 on
the Mantis ticket).

## Fix

Expose SNoiraud's 2018-12-20 workaround as a user-facing report option.
A new "Compact tree layout" boolean on the Sandclock report appends
`template=database pole reduced` to the `\genealogytree[…]` parameter
list when enabled, giving ~4x more space per page at the cost of
denser per-node formatting.

The directive is appended last so it overrides any node-spacing
defaults that `gramps/gen/plug/docgen/treedoc.py`'s built-in keys
(`level distance`, `node size`, `level size`) set earlier in the
parameter list — pgfkeys is order-sensitive, so a later `template=`
re-sets the keys the template controls.

Option-list assembly was extracted into a `_build_tree_options()`
method so the option-construction logic can be unit-tested without
driving a full report run.

## Verified against

- `GenealogyTree/gt_sandclock.py:148` — `_build_tree_options` body
- `GenealogyTree/gt_sandclock.py:96-128` — `write_report` calls into it
- `GenealogyTree/treeplugins.gpr.py` — unchanged; per addons-source
  convention the maintainer manages the `.gpr.py` version, not the
  contributor

## Test

`GenealogyTree/tests/test_sandclock_compact_template.py` — five focused
unit tests exercising the new option-list builder via
`SandclockTree.__new__(...)` skeleton (no Gramps GUI, no LaTeX, no DB).
Cover:

  * Default layout omits any `template=` directive.
  * `compact=True` appends exactly `template=database pole reduced`.
  * Directive lands LAST in the option list (pgfkeys order).
  * Other default options (`pref code=`, `list separators hang`,
    `place text=`, `box=`) remain intact when compact is on.
  * `include_images` and `compact` coexist without either suppressing
    the other.

All 5 pass via `gramps-testbed/scripts/ubuntu/run-addon-unit.sh
GenealogyTree`.

Fixes #10512

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
main() reads `self.uistate.viewmanager.active_page.bottombar`
unguarded, but the gramplet framework keeps stepping the generator
via `_updater` → `next(self._generator)` after the family tree is
closed. At that point `active_page` is None, so the read raises
AttributeError: 'NoneType' object has no attribute 'bottombar'.

Pull active_page into a local, return early if it's None, then
proceed with the existing bottombar/db-open/count<3 short-circuit
chain. No behaviour change while a tree is open.

Regression test exercises main() with a stub uistate whose
active_page is None and asserts the generator exits cleanly
(StopIteration) instead of raising. Two further cases lock the
existing dashboard / non-dashboard short-circuit paths so the new
guard does not regress them.

Fixes #13966
… off

The pre-pass guarded by `if self.dubperson:` in write_report() is the
only path that fills `self.report_app_ref`. When that option is off but
Index of Dates / Places / Names is on, append_event still runs from
write_person_info / __write_family_events for every event and raises
against the empty / missing table — KeyError on the current handle
(this report), or AttributeError on older addon versions before the
partial fix for 12857/12859 added the unconditional init.

Fall back inside append_event to the current writing-pass coordinates
(report_count, generation+1, dnumber[phandle], person's name). That is
the natural Ref for an index entry anyway — the location in the
document where the event is being narrated. When report_app_ref is
populated (the omit-duplicates path), the original `[0]` read is
preserved, so behaviour there is unchanged.

Regression test exercises append_event in isolation with an empty
report_app_ref and asserts (a) it does not raise and (b) the index
entry references current state.

Fixes #14051
Refines the previous commit. The earlier fallback computed the Ref
tuple from the CURRENT writing-pass coordinates each time
append_event ran. With omit-duplicates off and a person appearing in
multiple per-ascendant reports, append_event runs once per
encounter, and `self.index_of_dates[year][date]` / index_of_places
overwrites on every call — so the surviving entry ended up tagged
with the LAST encounter's coordinates, not the first.

The omit-duplicates path emits the FIRST-encounter coordinates
(report_app_ref[phandle][0]). Populating that entry on first call in
append_event — rather than re-deriving on every call — pins the Ref
to the first encounter and makes the two paths semantically
equivalent.

Verified empirically on example.gramps (I00016): 1365 / 1372 common
(place, date) index entries have identical Ref tuples between
omit-duplicates ON and OFF; the 7 mismatches all carry
`date="0000-00-00"`, a pre-existing key collision in
index_of_places[place][date] where unrelated empty-date events
overwrite each other (independent of this fix). Place coverage in
OFF is slightly higher (658 vs 656) because OFF doesn't early-return
duplicates, so events on duplicated people are indexed too.

New test TestRefSemanticsParityWithOmitDuplicates locks first-
encounter semantics by simulating a multi-encounter scenario.
PluginStatus.__info iterates Requirements().info(addon) as
[label, table] pairs and joins the first row with " ".join(req_lst[0]).
If the addon listing carries a present-but-empty requires key - e.g.
PostgreSQL Enhanced's gpr.py declares `requires_exe=[]`, which ends up
as `"re": []` in addons-<lang>.json - gramps core's Requirements.info
still emits an "Executables" label paired with an empty table, and
the join raises `IndexError: list index out of range`. Skip empty
tables cleanly: indexing fails on them and there is nothing useful to
render anyway.

PostgreSQL Enhanced is currently the only addon in gramps61 listings
with a present-but-empty requires key, which matches the reported
"only the PostgreSQL Enhanced row crashes" symptom.

Resolves #13979.
Running `make.py <ver> listing <Addon>` against an addon whose .gpr.py
declares `include_in_listing=False` (or whose .addon.tgz has not been
built yet) caused the listings file to be overwritten with `[]`,
wiping every previously listed addon for that language.

The single-addon update path builds `listings` per language, then
either replaces the file (for "listing all") or merges entries from
the existing file with the new ones. When the targeted addon yielded
no eligible plugin, `listings` was empty, the merge loop did not
iterate, and `output` was written as `[]`.

Guard the single-addon path: when `listings` is empty and a listings
file already exists, skip the write and tell the user that the
targeted addon is not eligible and how to remove an existing entry
on purpose (`make.py <ver> unlist <Addon>`).

Resolves #13694.
GaryGriffin flagged on PR 915 that the listings file still corrupts
for addons that ship multiple .gpr.py files (or multiple register()
calls per .gpr.py). The Form addon is the canonical example:
running `make.py gramps61 listing Form` ballooned addons-en.json
from 170 entries to 510 - every existing addon appearing three
times, one per Form-registered plugin.

Root cause: the merge path's outer `for plugin in sorted(listings, ...)`
loop re-read the entire existing listings file on each iteration
while accumulating into a shared `output`. With N new plugins for
cmd_arg the existing N-1 unrelated entries got appended N times.

Replace the per-plugin re-read with a single-pass merge: read the
existing file once, drop every row that belongs to cmd_arg (matched
by .z) or whose (t, i) collides with one of the fresh plugins, then
merge the kept rows with the new plugins in sorted (t, i) order.

This also tightens behaviour in two ways:
- Stale entries for cmd_arg (e.g. a register() removed from a
  gpr.py since the last build) are dropped, not preserved.
- The "first match wins" ambiguity of the old z+t matcher is gone;
  matching is per-plugin by (t, i).

Tests in `tests/test_make_listing.py` now cover both the 13694 case
(include_in_listing=False addon) and a multi-gpr addon shaped like
the real Form. The multi-gpr test fails on the pre-fix code with
"14 != 6" - exactly the symptom Gary saw - and passes after.

Resolves #13694.
The Geneanet entry in WEBSITES (FRWebPack.py:45) pointed at the
deprecated `https://search.geneanet.org/result.php?lang=fr&name=...`
URL, which no longer returns useful results, and the template only
carried the surname.

Replace with the current individus search the reporter on Mantis
14145 supplied:

  https://www.geneanet.org/fonds/individus/?go=1&nom=%(surname)s&prenom=%(given)s

The two `%(...)s` placeholders match the file's existing convention
(see lines 41, 46, 48) and the libwebconnect URL builder at
libwebconnect/libwebconnect.py:182, which formats the pattern with
the dict returned by make_person_dict — both `surname` and `given`
keys are populated there.

callmedave's note 4 on the tracker recommends the WebSearch
Gramplet as a longer-term replacement for the Web Connect Pack
family of addons, BUT his note 5 explicitly confirms the bug for
FrWebConnectPack — so the live addon is still in scope and this is
not a wontfix.

Add a regression test in `FRWebConnectPack/tests/` that pulls the
Geneanet pattern from WEBSITES and applies the same `pattern %
dict` formatting libwebconnect uses. Pure string assertion — no
network, no display, no Gtk. The test covers both halves of the
fix: the URL must include both name parts (the given name was
discarded pre-fix) and must target the current host/path/params
(the old `search.geneanet.org/result.php` form is asserted absent).

Verified via the testbed's `run-addon-unit.sh FRWebConnectPack`:

  Before fix: FAILED (failures=2)
  After fix:  2 tests, OK

Fixes #14145

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add IE 1926 Form and reference
B/M/D forms from https://gramps-project.org/bugs/view.php?id=13977

Removed Marriage/Signed field
Added new headings for additional attributes in the form.

Added new headings and reordered them.
@eduralph
eduralph force-pushed the sync/upstream-maintenance-gramps60-auto branch from 320d9d5 to 7aa8a9a Compare May 28, 2026 07:45
@eduralph
eduralph force-pushed the sync/upstream-maintenance-gramps60-auto branch from 7aa8a9a to 7b06efa Compare May 29, 2026 07:45
@eduralph
eduralph merged commit 400180d into maintenance/gramps60 May 29, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants