Skip to content

Commit 715c5d7

Browse files
eduralphclaude
authored andcommitted
RepositoriesReport: write repository URLs when "include URLs" is on
`RepositoryReportAlt.__write_repository` (RepositoriesReportAlt.py :198-200) had its URL-writing block commented out -- and the variable the commented code referenced (`internet`) was never defined anywhere, so a naive uncomment would raise `NameError: name 'internet' is not defined`. With the include-URLs option selected the report opened an Internet paragraph (via the outer `if self.inc_intern or self.inc_addres:` guard) but emitted nothing into it; per codefarmer on Mantis 13955 note 7 the block has been commented since 2010. Replace the stub with the URL section it should always have been. Compute the URLs from `repository.get_url_list()` -- the same attribute `tagreport.py:712` and `narrativeweb` use to find a repository's web addresses -- once per repository (URLs are not per-address) and write them in their own paragraph guarded by `inc_intern`. Hoist out of the per-address loop so a repository with zero addresses still gets its URLs, and a repository with multiple addresses doesn't have its URL list duplicated per iteration. Drop the now-stale outer `inc_intern or inc_addres` combined guard; each block has its own gate. Honour the rest of the report's empty-field convention: with `inc_intern` on and zero URLs the paragraph is suppressed unless `incl_empty` is also on (mirroring the `inc_addres` / `incl_empty` pattern at line 201). Add a regression test in `RepositoriesReport/tests/` that builds the report instance via `__new__`-bypass (so we don't need a real options/database/user wiring), stubs `self.doc` with a Mock, and calls the name-mangled `__write_repository` with a stubbed repository whose `get_url_list()` returns Url mocks. Three cases: URLs are written when `inc_intern` is on; no Internet paragraph when it's off; no Internet paragraph when there are no URLs and `incl_empty` is off. Pure unit test -- no display, no doc backend, no Gramps DB. Verified via the testbed's `run-addon-unit.sh RepositoriesReport`: Before fix: FAILED (failures=1) -- test_urls_written_when_inc_intern_on fails with `AssertionError: 'Internet: ' not found in ['Test Library (Library)']` After fix: 3 tests, OK Fixes #13955 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 41ec2a9 commit 715c5d7

2 files changed

Lines changed: 182 additions & 9 deletions

File tree

RepositoriesReport/RepositoriesReportAlt.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,20 +189,23 @@ def __write_repository(self, handle):
189189
# additional repository informations
190190

191191
child_list = repository.get_text_data_child_list()
192+
193+
if self.inc_intern:
194+
urls = [url.get_path() for url in repository.get_url_list()]
195+
if urls or self.incl_empty:
196+
self.doc.start_paragraph('REPO-Section2')
197+
self.doc.write_text(self._('Internet:') + space)
198+
self.doc.write_text("\n".join(urls))
199+
self.doc.end_paragraph()
200+
192201
addresses = repository.get_handle_referents()
193202
for address_handle in addresses:
194203
address = ReportUtils.get_address_str(address_handle)
195204

196-
if self.inc_intern or self.inc_addres:
205+
if self.inc_addres or self.incl_empty:
197206
self.doc.start_paragraph('REPO-Section2')
198-
199-
#if self.inc_intern:
200-
#self.doc.write_text(self._('Internet:'))
201-
#self.doc.write_text(internet)
202-
if self.inc_addres or self.incl_empty:
203-
self.doc.write_text(self._('Address:') + space)
204-
self.doc.write_text(address)
205-
207+
self.doc.write_text(self._('Address:') + space)
208+
self.doc.write_text(address)
206209
self.doc.end_paragraph()
207210

208211
def __write_referenced_sources(self, handle):
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#
2+
# Gramps - a GTK+/GNOME based genealogy program
3+
#
4+
# Copyright (C) 2026 Gramps Development Team
5+
#
6+
# This program is free software; you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation; either version 2 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19+
#
20+
21+
"""
22+
Regression test for bug 13955: RepositoriesReport omits URLs when the
23+
"include repositories urls" option is selected.
24+
25+
Historically `RepositoriesReportAlt.__write_repository` had the URL-
26+
writing block commented out (codefarmer, tracker note 7: commented
27+
since 2010, throws `NameError: name 'internet' is not defined` if you
28+
uncomment it naively at line 200). With include-URLs ON, the report
29+
silently produced an empty Internet paragraph -- the URLs the user
30+
asked for never reached the doc.
31+
32+
Construct the class via `__new__` (bypass `__init__` so we don't need
33+
options/database/user wiring or a real doc backend) and call the
34+
name-mangled `__write_repository` method with a stubbed repository,
35+
database, and doc. Assert the URL text reaches `doc.write_text` when
36+
`inc_intern` is on, and is suppressed when it's off.
37+
38+
Pure unit test: no display, no Gtk, no real Gramps DB.
39+
"""
40+
41+
import os
42+
import sys
43+
import unittest
44+
from unittest.mock import MagicMock
45+
46+
# Pin Gtk to 3.0 before importing -- RepositoriesReportAlt pulls in
47+
# gramps.gen.plug.docgen which transitively touches GTK-3-only enums.
48+
# Skip cleanly if GTK 3 is not available.
49+
try:
50+
import gi
51+
52+
gi.require_version("Gtk", "3.0")
53+
gi.require_version("Gdk", "3.0")
54+
except (ImportError, ValueError) as err:
55+
raise unittest.SkipTest("GTK 3.0 / PyGObject not available: %s" % err)
56+
57+
# Make sure addon modules are importable from the parent directory.
58+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
59+
60+
61+
def _make_repository(urls):
62+
"""Return a mock Repository with the given list of URL strings."""
63+
repo = MagicMock(name="Repository")
64+
repo.get_name.return_value = "Test Library"
65+
repo.get_type.return_value = "Library"
66+
repo.get_referenced_handles.return_value = []
67+
repo.get_text_data_child_list.return_value = []
68+
repo.get_handle_referents.return_value = []
69+
repo.handle = "REPO_HANDLE"
70+
71+
url_objects = []
72+
for path in urls:
73+
u = MagicMock(name="Url")
74+
u.get_path.return_value = path
75+
url_objects.append(u)
76+
repo.get_url_list.return_value = url_objects
77+
return repo
78+
79+
80+
def _make_report(repo, inc_intern, incl_empty=False):
81+
"""Construct a RepositoryReportAlt without running __init__.
82+
83+
The class' real __init__ wants an Options object, a database, and
84+
a User -- none of which we need to exercise the per-repository
85+
write path. Build a bare instance and pin only the attributes
86+
`__write_repository` reads.
87+
"""
88+
from RepositoriesReport import RepositoriesReportAlt as mod # pylint: disable=import-outside-toplevel
89+
90+
report = mod.RepositoryReportAlt.__new__(mod.RepositoryReportAlt)
91+
report.doc = MagicMock(name="doc")
92+
report._ = lambda s: s # identity i18n for assertion clarity
93+
report.database = MagicMock(name="database")
94+
report.database.get_repository_from_handle.return_value = repo
95+
report.inc_intern = inc_intern
96+
report.inc_addres = False
97+
report.inclu_note = False
98+
report.incl_empty = incl_empty
99+
return report
100+
101+
102+
class TestRepositoryUrlOutput(unittest.TestCase):
103+
"""Regression for bug 13955."""
104+
105+
def _written_text(self, report):
106+
return [call.args[0] for call in report.doc.write_text.call_args_list]
107+
108+
def test_urls_written_when_inc_intern_on(self):
109+
"""When include-URLs is selected the repository URLs reach
110+
the doc.
111+
112+
Pre-fix this fails: the URL block was commented out so the
113+
Internet paragraph never carried any URL text.
114+
"""
115+
repo = _make_repository(["https://example.org/", "https://example.net/"])
116+
report = _make_report(repo, inc_intern=True)
117+
report._RepositoryReportAlt__write_repository("REPO_HANDLE")
118+
119+
written = self._written_text(report)
120+
self.assertIn(
121+
"Internet: ",
122+
written,
123+
"Internet label must appear when inc_intern is on",
124+
)
125+
self.assertIn(
126+
"https://example.org/\nhttps://example.net/",
127+
written,
128+
"Both repository URLs must reach the doc",
129+
)
130+
131+
def test_no_url_paragraph_when_inc_intern_off(self):
132+
"""With include-URLs off the Internet paragraph is suppressed
133+
entirely (no empty paragraph, no Internet label).
134+
135+
Pre-fix behaviour also suppressed the URLs in this case --
136+
same outcome -- but the regression guard locks the gate so a
137+
later "always write the section" refactor wouldn't slip
138+
through.
139+
"""
140+
repo = _make_repository(["https://example.org/"])
141+
report = _make_report(repo, inc_intern=False)
142+
report._RepositoryReportAlt__write_repository("REPO_HANDLE")
143+
144+
written = self._written_text(report)
145+
self.assertNotIn("Internet: ", written)
146+
for text in written:
147+
self.assertNotIn(
148+
"https://example.org/",
149+
text,
150+
"URL must not appear when inc_intern is off",
151+
)
152+
153+
def test_no_url_paragraph_when_no_urls_and_incl_empty_off(self):
154+
"""A repository with no URLs and incl_empty off must not get
155+
an empty Internet paragraph.
156+
157+
This pins the contract: the URL section follows the same
158+
"empty fields suppressed unless incl_empty" rule the rest of
159+
the report uses (see inc_addres / incl_empty pattern).
160+
"""
161+
repo = _make_repository([])
162+
report = _make_report(repo, inc_intern=True, incl_empty=False)
163+
report._RepositoryReportAlt__write_repository("REPO_HANDLE")
164+
165+
written = self._written_text(report)
166+
self.assertNotIn("Internet: ", written)
167+
168+
169+
if __name__ == "__main__":
170+
unittest.main()

0 commit comments

Comments
 (0)