8649: Familygroup report: Add filter option
This commit is contained in:
parent
6b86b154fb
commit
7f41373f07
@ -344,10 +344,12 @@ class DbReadBase(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_family_handles(self):
|
||||
def get_family_handles(self, sort_handles=False):
|
||||
"""
|
||||
Return a list of database handles, one handle for each Family in
|
||||
the database.
|
||||
|
||||
If sort_handles is True, the list is sorted by surnames.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -27,7 +27,8 @@ CustomFilters = None
|
||||
|
||||
from ..const import CUSTOM_FILTERS
|
||||
from ._filterlist import FilterList
|
||||
from ._genericfilter import GenericFilter, GenericFilterFactory, DeferredFilter
|
||||
from ._genericfilter import (GenericFilter, GenericFilterFactory,
|
||||
DeferredFilter, DeferredFamilyFilter)
|
||||
from ._paramfilter import ParamFilter
|
||||
from ._searchfilter import SearchFilter, ExactSearchFilter
|
||||
|
||||
|
@ -406,3 +406,27 @@ class DeferredFilter(GenericFilter):
|
||||
if self.name_pair[1]:
|
||||
return self._(self.name_pair[0]) % self.name_pair[1]
|
||||
return self._(self.name_pair[0])
|
||||
|
||||
class DeferredFamilyFilter(GenericFamilyFilter):
|
||||
"""
|
||||
Filter class allowing for deferred translation of the filter name
|
||||
"""
|
||||
|
||||
def __init__(self, filter_name, family_name):
|
||||
GenericFamilyFilter.__init__(self, None)
|
||||
self.name_pair = [filter_name, family_name]
|
||||
|
||||
def get_name(self, ulocale=glocale):
|
||||
"""
|
||||
return the filter name, possibly translated
|
||||
|
||||
If ulocale is passed in (a :class:`.GrampsLocale`) then
|
||||
the translated value will be returned instead.
|
||||
|
||||
:param ulocale: allow deferred translation of strings
|
||||
:type ulocale: a :class:`.GrampsLocale` instance
|
||||
"""
|
||||
self._ = ulocale.translation.gettext
|
||||
if self.name_pair[1]:
|
||||
return self._(self.name_pair[0]) % self.name_pair[1]
|
||||
return self._(self.name_pair[0])
|
||||
|
@ -5,7 +5,7 @@
|
||||
# Copyright (C) 2007-2009 Brian G. Matherly
|
||||
# Copyright (C) 2008 James Friedmann <jfriedmannj@gmail.com>
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2015 Paul Franklin
|
||||
# Copyright (C) 2015-2016 Paul Franklin
|
||||
#
|
||||
# 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
|
||||
@ -309,3 +309,77 @@ def get_person_filters(person, include_single=True, name_format=None):
|
||||
the_filters = [all, des, df, ans, com]
|
||||
the_filters.extend(CustomFilters.get_filters('Person'))
|
||||
return the_filters
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Family Filters
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
def get_family_filters(database, family,
|
||||
include_single=True, name_format=None):
|
||||
"""
|
||||
Return a list of filters that are relevant for the given family
|
||||
|
||||
:param database: The database that the family is in.
|
||||
:type database: DbBase
|
||||
:param family: the family the filters should apply to.
|
||||
:type family: :class:`~.family.Family`
|
||||
:param include_single: include a filter to include the single family
|
||||
:type include_single: boolean
|
||||
:param name_format: optional format to control display of person's name
|
||||
:type name_format: None or int
|
||||
"""
|
||||
from ...filters import (GenericFilterFactory, rules, CustomFilters,
|
||||
DeferredFamilyFilter)
|
||||
from ...display.name import displayer as name_displayer
|
||||
|
||||
if family:
|
||||
real_format = name_displayer.get_default_format()
|
||||
if name_format is not None:
|
||||
name_displayer.set_default_format(name_format)
|
||||
fhandle = family.get_father_handle()
|
||||
if fhandle:
|
||||
father = database.get_person_from_handle(fhandle)
|
||||
father_name = name_displayer.display(father)
|
||||
else:
|
||||
father_name = _("unknown father")
|
||||
mhandle = family.get_mother_handle()
|
||||
if mhandle:
|
||||
mother = database.get_person_from_handle(mhandle)
|
||||
mother_name = name_displayer.display(mother)
|
||||
else:
|
||||
mother_name = _("unknown mother")
|
||||
gramps_id = family.get_gramps_id()
|
||||
name = _("%(father_name)s and %(mother_name)s (%(family_id)s)") % {
|
||||
'father_name': father_name,
|
||||
'mother_name': mother_name,
|
||||
'family_id': gramps_id}
|
||||
name_displayer.set_default_format(real_format)
|
||||
else:
|
||||
# Do this in case of command line options query (show=filter)
|
||||
name = _("FAMILY")
|
||||
gramps_id = ''
|
||||
|
||||
if include_single:
|
||||
FilterClass = GenericFilterFactory('Family')
|
||||
filt_id = FilterClass()
|
||||
filt_id.set_name(name)
|
||||
filt_id.add_rule(rules.family.HasIdOf([gramps_id]))
|
||||
|
||||
all = DeferredFamilyFilter(_T_("Every family"), None)
|
||||
all.add_rule(rules.family.AllFamilies([]))
|
||||
|
||||
# feature request 2356: avoid genitive form
|
||||
df = DeferredFamilyFilter(_T_("Descendant Families of %s"), name)
|
||||
df.add_rule(rules.family.IsDescendantOf([gramps_id, 1]))
|
||||
|
||||
# feature request 2356: avoid genitive form
|
||||
ans = DeferredFamilyFilter(_T_("Ancestor Families of %s"), name)
|
||||
ans.add_rule(rules.family.IsAncestorOf([gramps_id, 1]))
|
||||
|
||||
if include_single:
|
||||
the_filters = [filt_id, all, df, ans]
|
||||
else:
|
||||
the_filters = [all, df, ans]
|
||||
the_filters.extend(CustomFilters.get_filters('Family'))
|
||||
return the_filters
|
||||
|
@ -548,11 +548,12 @@ class FilterProxyDb(ProxyDbBase):
|
||||
else:
|
||||
return map(self.get_event_from_handle, self.elist)
|
||||
|
||||
def get_family_handles(self):
|
||||
def get_family_handles(self, sort_handles=False):
|
||||
"""
|
||||
Return a list of database handles, one handle for each Family in
|
||||
the database.
|
||||
the database. If sort_handles is True, the list is sorted by surnames
|
||||
"""
|
||||
# FIXME: flist is not a sorted list of handles
|
||||
return list(self.flist)
|
||||
|
||||
def iter_family_handles(self):
|
||||
|
@ -319,18 +319,20 @@ class ProxyDbBase(DbReadBase):
|
||||
def get_person_handles(self, sort_handles=False):
|
||||
"""
|
||||
Return a list of database handles, one handle for each Person in
|
||||
the database.
|
||||
the database. If sort_handles is True, the list is sorted by surnames
|
||||
"""
|
||||
# FIXME: this is not a sorted list of handles
|
||||
if self.db.is_open:
|
||||
return list(self.iter_person_handles())
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_family_handles(self, sort_handles=True):
|
||||
def get_family_handles(self, sort_handles=False):
|
||||
"""
|
||||
Return a list of database handles, one handle for each Family in
|
||||
the database.
|
||||
the database. If sort_handles is True, the list is sorted by surnames
|
||||
"""
|
||||
# FIXME: this is not a sorted list of handles
|
||||
if self.db.is_open:
|
||||
return list(self.iter_family_handles())
|
||||
else:
|
||||
|
@ -110,6 +110,16 @@ def find_byte_surname(key, data):
|
||||
return surn.encode('utf-8')
|
||||
return surn
|
||||
|
||||
def find_fullname(key, data):
|
||||
"""
|
||||
Creating a fullname from raw data of a person, to use for sort and index
|
||||
returns a byte string
|
||||
"""
|
||||
fullname_data = [(data[3][5][0][0] + ' ' + data[3][4], # surname givenname
|
||||
data[3][5][0][1], data[3][5][0][2],
|
||||
data[3][5][0][3], data[3][5][0][4])]
|
||||
return __index_surname(fullname_data)
|
||||
|
||||
def find_surname(key, data):
|
||||
"""
|
||||
Creating a surname from raw data of a person, to use for sort and index
|
||||
@ -254,7 +264,7 @@ class DbBsddbRead(DbReadBase, Callback):
|
||||
.. method:: get_<object>_handles()
|
||||
|
||||
returns a list of handles for the object type, optionally sorted
|
||||
(for Person, Place, Source and Media objects)
|
||||
(for Citation, Family, Media, Person, Place, Source, and Tag objects)
|
||||
|
||||
.. method:: iter_<object>_handles()
|
||||
|
||||
@ -1143,16 +1153,21 @@ class DbBsddbRead(DbReadBase, Callback):
|
||||
return self.all_handles(self.event_map)
|
||||
return []
|
||||
|
||||
def get_family_handles(self):
|
||||
def get_family_handles(self, sort_handles=False):
|
||||
"""
|
||||
Return a list of database handles, one handle for each Family in
|
||||
the database.
|
||||
|
||||
If sort_handles is True, the list is sorted by surnames.
|
||||
|
||||
.. warning:: For speed the keys are directly returned, so on python3
|
||||
bytestrings are returned!
|
||||
"""
|
||||
if self.db_is_open:
|
||||
return self.all_handles(self.family_map)
|
||||
handle_list = self.all_handles(self.family_map)
|
||||
if sort_handles:
|
||||
handle_list.sort(key=self.__sortbyfamily_key)
|
||||
return handle_list
|
||||
return []
|
||||
|
||||
def get_repository_handles(self):
|
||||
@ -1804,6 +1819,24 @@ class DbBsddbRead(DbReadBase, Callback):
|
||||
return glocale.sort_key(find_surname(handle,
|
||||
self.person_map.get(handle)))
|
||||
|
||||
def __sortbyfamily_key(self, handle):
|
||||
if isinstance(handle, str):
|
||||
handle = handle.encode('utf-8')
|
||||
data = self.family_map.get(handle)
|
||||
data2 = data[2]
|
||||
if isinstance(data2, str):
|
||||
data2 = data2.encode('utf-8')
|
||||
data3 = data[3]
|
||||
if isinstance(data3, str):
|
||||
data3 = data3.encode('utf-8')
|
||||
if data2: # father handle
|
||||
return glocale.sort_key(find_fullname(data2,
|
||||
self.person_map.get(data2)))
|
||||
elif data3: # mother handle
|
||||
return glocale.sort_key(find_fullname(data3,
|
||||
self.person_map.get(data3)))
|
||||
return ''
|
||||
|
||||
def __sortbyplace(self, first, second):
|
||||
if isinstance(first, str):
|
||||
first = first.encode('utf-8')
|
||||
|
@ -158,7 +158,8 @@ class DictionaryDb(DbGeneric):
|
||||
## Fixme: implement sort
|
||||
return [bytes(key, "utf-8") for key in self._person_dict.keys()]
|
||||
|
||||
def get_family_handles(self):
|
||||
def get_family_handles(self, sort_handles=False):
|
||||
## Fixme: implement sort
|
||||
return [bytes(key, "utf-8") for key in self._family_dict.keys()]
|
||||
|
||||
def get_event_handles(self):
|
||||
|
@ -4,8 +4,8 @@
|
||||
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
||||
# Copyright (C) 2010 Jakim Friant
|
||||
# Copyright (C) 2013-2016 Paul Franklin
|
||||
# Copyright (C) 2015 Gerald Kunzmann <gerald@gkunzmann.de>
|
||||
# Copyright (C) 2013-2016 Paul Franklin
|
||||
#
|
||||
# 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
|
||||
@ -39,7 +39,7 @@ from functools import partial
|
||||
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
||||
_ = glocale.translation.sgettext
|
||||
from gramps.gen.lib import EventRoleType, EventType, NoteType, Person
|
||||
from gramps.gen.plug.menu import BooleanOption, FamilyOption
|
||||
from gramps.gen.plug.menu import BooleanOption, FamilyOption, FilterOption
|
||||
from gramps.gen.plug.report import Report
|
||||
from gramps.gen.plug.report import utils as ReportUtils
|
||||
from gramps.gen.plug.report import MenuReportOptions
|
||||
@ -70,7 +70,9 @@ class FamilyGroup(Report):
|
||||
This report needs the following parameters (class variables)
|
||||
that come in the options class.
|
||||
|
||||
family_handle - Handle of the family to write report on.
|
||||
filter - Filter to be applied to the families of the database.
|
||||
The option class carries its number, and the function
|
||||
returning the list of filters.
|
||||
includeAttrs - Whether to include attributes
|
||||
name_format - Preferred format to display names
|
||||
incl_private - Whether to include private data
|
||||
@ -78,20 +80,14 @@ class FamilyGroup(Report):
|
||||
years_past_death - Consider as living this many years after death
|
||||
"""
|
||||
Report.__init__(self, database, options, user)
|
||||
self._user = user
|
||||
menu = options.menu
|
||||
|
||||
stdoptions.run_private_data_option(self, menu)
|
||||
stdoptions.run_living_people_option(self, menu)
|
||||
self.db = self.database
|
||||
|
||||
self.family_handle = None
|
||||
|
||||
family_id = menu.get_option_by_name('family_id').get_value()
|
||||
family = self.db.get_family_from_gramps_id(family_id)
|
||||
if family:
|
||||
self.family_handle = family.get_handle()
|
||||
else:
|
||||
self.family_handle = None
|
||||
self.filter = menu.get_option_by_name('filter').get_filter()
|
||||
|
||||
get_option_by_name = menu.get_option_by_name
|
||||
get_value = lambda name:get_option_by_name(name).get_value()
|
||||
@ -109,8 +105,8 @@ class FamilyGroup(Report):
|
||||
self.incChiMar = get_value('incChiMar')
|
||||
self.includeAttrs = get_value('incattrs')
|
||||
|
||||
rlocale = self.set_locale(get_value('trans'))
|
||||
self._ = rlocale.translation.sgettext # needed for English
|
||||
self._locale = self.set_locale(get_value('trans'))
|
||||
self._ = self._locale.translation.sgettext # needed for English
|
||||
|
||||
stdoptions.run_name_format_option(self, menu)
|
||||
|
||||
@ -654,8 +650,22 @@ class FamilyGroup(Report):
|
||||
self.dump_family(child_family_handle, (generation+1))
|
||||
|
||||
def write_report(self):
|
||||
if self.family_handle:
|
||||
self.dump_family(self.family_handle, 1)
|
||||
flist = self.db.get_family_handles(sort_handles=True)
|
||||
if not self.filter:
|
||||
fam_list = flist
|
||||
else:
|
||||
with self._user.progress(_('Family Group Report'),
|
||||
_('Applying filter...'),
|
||||
self.db.get_number_of_families()) as step:
|
||||
fam_list = self.filter.apply(self.db, flist, step)
|
||||
if fam_list:
|
||||
with self._user.progress(_('Family Group Report'),
|
||||
_('Writing families'),
|
||||
len(fam_list)) as step:
|
||||
for family_handle in fam_list:
|
||||
self.dump_family(family_handle, 1)
|
||||
self.doc.page_break()
|
||||
step()
|
||||
else:
|
||||
self.doc.start_paragraph('FGR-Title')
|
||||
self.doc.write_text(self._("Family Group Report"))
|
||||
@ -663,7 +673,7 @@ class FamilyGroup(Report):
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
#
|
||||
# MenuReportOptions
|
||||
# FamilyGroupOptions
|
||||
#
|
||||
#------------------------------------------------------------------------
|
||||
class FamilyGroupOptions(MenuReportOptions):
|
||||
@ -673,6 +683,10 @@ class FamilyGroupOptions(MenuReportOptions):
|
||||
"""
|
||||
|
||||
def __init__(self, name, dbase):
|
||||
self.__db = dbase
|
||||
self.__fid = None
|
||||
self.__filter = None
|
||||
self.__recursive = None
|
||||
MenuReportOptions.__init__(self, name, dbase)
|
||||
|
||||
def add_menu_options(self, menu):
|
||||
@ -682,20 +696,30 @@ class FamilyGroupOptions(MenuReportOptions):
|
||||
add_option = partial(menu.add_option, category_name)
|
||||
##########################
|
||||
|
||||
family_id = FamilyOption(_("Center Family"))
|
||||
family_id.set_help(_("The center family for the report"))
|
||||
add_option("family_id", family_id)
|
||||
self.__filter = FilterOption(_("Filter"), 0)
|
||||
self.__filter.set_help(
|
||||
_("Select the filter to be applied to the report."))
|
||||
add_option("filter", self.__filter)
|
||||
self.__filter.connect('value-changed', self.__filter_changed)
|
||||
|
||||
stdoptions.add_name_format_option(menu, category_name)
|
||||
self.__fid = FamilyOption(_("Center Family"))
|
||||
self.__fid.set_help(_("The center family for the filter"))
|
||||
add_option("family_id", self.__fid)
|
||||
self.__fid.connect('value-changed', self.__update_filters)
|
||||
|
||||
self._nf = stdoptions.add_name_format_option(menu, category_name)
|
||||
self._nf.connect('value-changed', self.__update_filters)
|
||||
|
||||
self.__update_filters()
|
||||
|
||||
stdoptions.add_private_data_option(menu, category_name)
|
||||
|
||||
stdoptions.add_living_people_option(menu, category_name)
|
||||
|
||||
recursive = BooleanOption(_('Recursive'), False)
|
||||
recursive.set_help(_("Create reports for all descendants "
|
||||
"of this family."))
|
||||
add_option("recursive", recursive)
|
||||
self.__recursive = BooleanOption(_('Recursive (down)'), False)
|
||||
self.__recursive.set_help(_("Create reports for all descendants "
|
||||
"of this family."))
|
||||
add_option("recursive", self.__recursive)
|
||||
|
||||
stdoptions.add_localization_option(menu, category_name)
|
||||
|
||||
@ -763,6 +787,35 @@ class FamilyGroupOptions(MenuReportOptions):
|
||||
"information."))
|
||||
add_option("missinginfo", missinginfo)
|
||||
|
||||
def __update_filters(self):
|
||||
"""
|
||||
Update the filter list based on the selected family
|
||||
"""
|
||||
fid = self.__fid.get_value()
|
||||
family = self.__db.get_family_from_gramps_id(fid)
|
||||
nfv = self._nf.get_value()
|
||||
filter_list = ReportUtils.get_family_filters(self.__db, family,
|
||||
include_single=True,
|
||||
name_format=nfv)
|
||||
self.__filter.set_filters(filter_list)
|
||||
|
||||
def __filter_changed(self):
|
||||
"""
|
||||
Handle filter change.
|
||||
If the filter is not family-specific, disable the family option
|
||||
"""
|
||||
filter_value = self.__filter.get_value()
|
||||
if filter_value in [0, 2, 3]: # filters that rely on the center family
|
||||
self.__fid.set_available(True)
|
||||
else: # filters that don't
|
||||
self.__fid.set_available(False)
|
||||
# only allow recursion if the center family is the only family
|
||||
if self.__recursive and filter_value == 0:
|
||||
self.__recursive.set_available(True)
|
||||
elif self.__recursive:
|
||||
self.__recursive.set_value(False)
|
||||
self.__recursive.set_available(False)
|
||||
|
||||
def make_default_style(self, default_style):
|
||||
"""Make default output style for the Family Group Report."""
|
||||
para = ParagraphStyle()
|
||||
|
Loading…
Reference in New Issue
Block a user