2121"""Functions for running Gramps reports."""
2222
2323import os
24+ import sys
2425import uuid
2526from pathlib import Path
2627from typing import Dict , Optional
2728
2829from flask import abort , current_app
2930from gramps .cli .plug import CommandLineReport
3031from gramps .cli .user import User
32+ from gramps .gen .const import GRAMPS_LOCALE as glocale
3133from gramps .gen .db .base import DbReadBase
34+ from gramps .gen .display .name import displayer as name_displayer
3235from gramps .gen .filters import reload_custom_filters
3336from gramps .gen .plug import BasePluginManager
3437from gramps .gen .plug .docgen import PaperStyle
38+ from gramps .gen .plug .menu import (
39+ BooleanOption ,
40+ DestinationOption ,
41+ EnumeratedListOption ,
42+ FamilyOption ,
43+ MediaOption ,
44+ NoteOption ,
45+ NumberOption ,
46+ Option ,
47+ PersonListOption ,
48+ PersonOption ,
49+ StringOption ,
50+ TextOption ,
51+ )
3552from gramps .gen .plug .report import (
3653 CATEGORY_BOOK ,
3754 CATEGORY_DRAW ,
4562from ..const import MIME_TYPES , REPORT_DEFAULTS , REPORT_FILTERS
4663from .util import abort_with_message
4764
65+ _ = glocale .translation .gettext
66+
4867_EXTENSION_MAP = {".gvpdf" : ".pdf" , ".gspdf" : ".pdf" }
4968
5069
5170def get_report_profile (
52- db_handle : DbReadBase , plugin_manager : BasePluginManager , report_data
71+ db_handle : DbReadBase ,
72+ plugin_manager : BasePluginManager ,
73+ report_data ,
74+ include_options_help : bool = True ,
5375):
5476 """Extract and return report attributes and options."""
5577 module = plugin_manager .load_plugin (report_data )
5678 option_class = getattr (module , report_data .optionclass )
57- report = CommandLineReport (
58- db_handle , report_data .name , report_data .category , option_class , {}
79+ report = ModifiedCommandLineReport (
80+ db_handle ,
81+ report_data .name ,
82+ report_data .category ,
83+ option_class ,
84+ {},
85+ include_options_help = include_options_help ,
5986 )
60- options_help = report .options_help
61- if REPORT_FILTERS :
62- for report_type in REPORT_FILTERS :
63- for item in options_help ["off" ][2 ]:
64- if item [: len (report_type )] == report_type :
65- del options_help ["off" ][2 ][options_help ["off" ][2 ].index (item )]
66- break
67- return {
87+ result = {
6888 "authors" : report_data .authors ,
6989 "authors_email" : report_data .authors_email ,
7090 "category" : report_data .category ,
7191 "description" : report_data .description ,
7292 "id" : report_data .id ,
7393 "name" : report_data .name ,
7494 "options_dict" : report .options_dict ,
75- "options_help" : options_help ,
7695 "report_modes" : report_data .report_modes ,
7796 "version" : report_data .version ,
7897 }
98+ if include_options_help :
99+ options_help = report .options_help
100+ if REPORT_FILTERS :
101+ for report_type in REPORT_FILTERS :
102+ for item in options_help ["off" ][2 ]:
103+ if item [: len (report_type )] == report_type :
104+ del options_help ["off" ][2 ][options_help ["off" ][2 ].index (item )]
105+ break
106+ result ["options_help" ] = options_help
107+ return result
79108
80109
81- def get_reports (db_handle : DbReadBase , report_id : str = None ):
110+ def get_reports (
111+ db_handle : DbReadBase , report_id : str = None , include_options_help : bool = True
112+ ):
82113 """Extract and return report attributes and options."""
83114 reload_custom_filters ()
84115 plugin_manager = BasePluginManager .get_instance ()
@@ -88,7 +119,12 @@ def get_reports(db_handle: DbReadBase, report_id: str = None):
88119 continue
89120 if report_data .category not in REPORT_DEFAULTS :
90121 continue
91- report = get_report_profile (db_handle , plugin_manager , report_data )
122+ report = get_report_profile (
123+ db_handle ,
124+ plugin_manager ,
125+ report_data ,
126+ include_options_help = include_options_help ,
127+ )
92128 reports .append (report )
93129 return reports
94130
@@ -103,6 +139,124 @@ def check_report_id_exists(report_id: str) -> bool:
103139 return False
104140
105141
142+ class ModifiedCommandLineReport (CommandLineReport ):
143+ """Patched version of gramps.cli.plug.CommandLineReport.
144+
145+ Avoids calling get_person_from_handle (individual database
146+ calls) on every person in the database."""
147+
148+ def __init__ (self , * args , ** kwargs ):
149+ """Initialize report."""
150+ self ._name_dict = {}
151+ self .include_options_help = kwargs .pop ("include_options_help" , True )
152+ super ().__init__ (* args , ** kwargs )
153+
154+ def _get_name_dict (self ):
155+ """Get a dictionary with all names in the database and cache it."""
156+ if not self ._name_dict :
157+ self ._name_dict = {
158+ person .handle : {
159+ "gramps_id" : person .gramps_id ,
160+ "name" : name_displayer .display (person ),
161+ }
162+ for person in self .database .iter_people ()
163+ }
164+ return self ._name_dict
165+
166+ def init_report_options_help (self ):
167+ """
168+ Initialize help for the options that are defined by each report.
169+ (And also any docgen options, if defined by the docgen.)
170+ """
171+ if not self .include_options_help :
172+ return
173+ if not hasattr (self .option_class , "menu" ):
174+ return
175+ menu = self .option_class .menu
176+
177+ for name in menu .get_all_option_names ():
178+ option = menu .get_option_by_name (name )
179+ self .options_help [name ] = ["" , option .get_help ()]
180+
181+ if isinstance (option , PersonOption ):
182+ name_dict = self ._get_name_dict ()
183+ handles = self .database .get_person_handles (True )
184+ id_list = [
185+ f"{ name_dict [handle ]['gramps_id' ]} \t { name_dict [handle ]['name' ]} "
186+ for handle in handles
187+ ]
188+ self .options_help [name ].append (id_list )
189+ elif isinstance (option , FamilyOption ):
190+ id_list = []
191+ name_dict = self ._get_name_dict ()
192+ for family in self .database .iter_families ():
193+ mname = ""
194+ fname = ""
195+ mhandle = family .get_mother_handle ()
196+ if mhandle :
197+ mname = name_dict .get (mhandle , {}).get ("name" , "" )
198+ fhandle = family .get_father_handle ()
199+ if fhandle :
200+ fname = name_dict .get (fhandle , {}).get ("name" , "" )
201+ # Translators: needed for French, Hebrew and Arabic
202+ text = _ (f"{ family .gramps_id } :\t { fname } , { mname } " )
203+ id_list .append (text )
204+ self .options_help [name ].append (id_list )
205+ elif isinstance (option , NoteOption ):
206+ id_list = []
207+ for note in self .database .iter_notes ():
208+ id_list .append (note .get_gramps_id ())
209+ self .options_help [name ].append (id_list )
210+ elif isinstance (option , MediaOption ):
211+ id_list = []
212+ for mobject in self .database .iter_media ():
213+ id_list .append (mobject .get_gramps_id ())
214+ self .options_help [name ].append (id_list )
215+ elif isinstance (option , PersonListOption ):
216+ self .options_help [name ].append ("" )
217+ elif isinstance (option , NumberOption ):
218+ self .options_help [name ].append ("A number" )
219+ elif isinstance (option , BooleanOption ):
220+ self .options_help [name ].append (["False" , "True" ])
221+ elif isinstance (option , DestinationOption ):
222+ self .options_help [name ].append ("A file system path" )
223+ elif isinstance (option , StringOption ):
224+ self .options_help [name ].append ("Any text" )
225+ elif isinstance (option , TextOption ):
226+ self .options_help [name ].append (
227+ "A list of text values. Each entry in the list "
228+ "represents one line of text."
229+ )
230+ elif isinstance (option , EnumeratedListOption ):
231+ ilist = []
232+ for value , description in option .get_items ():
233+ tabs = "\t "
234+ try :
235+ tabs = "\t \t " if len (value ) < 10 else "\t "
236+ except TypeError : # Value is a number, use just one tab.
237+ pass
238+ val = "%s%s%s" % (value , tabs , description )
239+ ilist .append (val )
240+ self .options_help [name ].append (ilist )
241+ elif isinstance (option , Option ):
242+ self .options_help [name ].append (option .get_help ())
243+ else :
244+ print (_ ("Unknown option: %s" ) % option , file = sys .stderr )
245+ print (
246+ _ (" Valid options are:" )
247+ + _ (", " ).join (list (self .options_dict .keys ())), # Arabic OK
248+ file = sys .stderr ,
249+ )
250+ print (
251+ _ (
252+ " Use '%(donottranslate)s' to see description "
253+ "and acceptable values"
254+ )
255+ % {"donottranslate" : "show=option" },
256+ file = sys .stderr ,
257+ )
258+
259+
106260def cl_report_new (
107261 database ,
108262 name ,
@@ -116,7 +270,9 @@ def cl_report_new(
116270
117271 Derived from gramps.cli.plug.cl_report.
118272 """
119- clr = CommandLineReport (database , name , category , options_class , options_str_dict )
273+ clr = ModifiedCommandLineReport (
274+ database , name , category , options_class , options_str_dict
275+ )
120276 if category in [CATEGORY_TEXT , CATEGORY_DRAW , CATEGORY_BOOK ]:
121277 if clr .doc_options :
122278 clr .option_class .handler .doc = clr .format (
0 commit comments