3220: Improvements for Records Report (Reinhard, PaulFranklin)

svn: r16981
This commit is contained in:
Jérôme Rapinat 2011-03-30 08:11:06 +00:00
parent 2bc580b73d
commit 77d823f0c1

View File

@ -35,12 +35,14 @@ from gen.ggettext import sgettext as _
# GRAMPS modules # GRAMPS modules
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
from gen.lib import ChildRefType, Date, Name from gen.lib import ChildRefType, Date, EventType, Name, StyledText, \
from gen.plug.docgen import FontStyle, ParagraphStyle, FONT_SANS_SERIF StyledTextTag, StyledTextTagType
from gen.plug.docgen import FontStyle, ParagraphStyle, FONT_SANS_SERIF, \
PARA_ALIGN_CENTER
from gen.display.name import displayer as name_displayer from gen.display.name import displayer as name_displayer
from gen.plug import Gramplet from gen.plug import Gramplet
from gen.plug.menu import (BooleanOption, EnumeratedListOption, from gen.plug.menu import (BooleanOption, EnumeratedListOption,
FilterOption, PersonOption) FilterOption, PersonOption, StringOption)
from gen.plug.report import Report from gen.plug.report import Report
from gen.plug.report import utils as ReportUtils from gen.plug.report import utils as ReportUtils
from gui.plug.report import MenuReportOptions from gui.plug.report import MenuReportOptions
@ -118,7 +120,7 @@ def _find_records(db, filter, callname):
# Birth date unknown or incomplete, so we can't calculate any age. # Birth date unknown or incomplete, so we can't calculate any age.
continue continue
name = _person_get_display_name(person, callname) name = _Person_get_styled_primary_name(person, callname)
if death_date is None: if death_date is None:
if probably_alive(person, db): if probably_alive(person, db):
@ -213,15 +215,17 @@ def _find_records(db, filter, callname):
father = db.get_person_from_handle(father_handle) father = db.get_person_from_handle(father_handle)
mother = db.get_person_from_handle(mother_handle) mother = db.get_person_from_handle(mother_handle)
name = _("%(father)s and %(mother)s") % { name = StyledText("").join([
'father' : _person_get_display_name(father, callname), _Person_get_styled_primary_name(father, callname),
'mother' : _person_get_display_name(mother, callname) } _(" and "),
_Person_get_styled_primary_name(mother, callname)])
_record(None, family_mostchildren, _record(None, family_mostchildren,
len(family.get_child_ref_list()), len(family.get_child_ref_list()),
name, 'Family', family.handle) name, 'Family', family.handle)
marriage_date = None marriage_date = None
divorce = None
divorce_date = None divorce_date = None
for event_ref in family.get_event_ref_list(): for event_ref in family.get_event_ref_list():
event = db.get_event_from_handle(event_ref.ref) event = db.get_event_from_handle(event_ref.ref)
@ -232,6 +236,7 @@ def _find_records(db, filter, callname):
if (event and event.get_type().is_divorce() and if (event and event.get_type().is_divorce() and
(event_ref.get_role().is_family() or (event_ref.get_role().is_family() or
event_ref.get_role().is_primary())): event_ref.get_role().is_primary())):
divorce = event
divorce_date = event.get_date_object() divorce_date = event.get_date_object()
father_death_date = _find_death_date(db, father) father_death_date = _find_death_date(db, father)
@ -241,6 +246,18 @@ def _find_records(db, filter, callname):
# Not married or marriage date unknown # Not married or marriage date unknown
continue continue
if divorce is not None and not _good_date(divorce_date):
# Divorced but date unknown or inexact
continue
if not probably_alive(father, db) and not _good_date(father_death_date):
# Father died but death date unknown or inexact
continue
if not probably_alive(mother, db) and not _good_date(mother_death_date):
# Mother died but death date unknown or inexact
continue
if divorce_date is None and father_death_date is None and mother_death_date is None: if divorce_date is None and father_death_date is None and mother_death_date is None:
# Still married and alive # Still married and alive
if probably_alive(father, db) and probably_alive(mother, db): if probably_alive(father, db) and probably_alive(mother, db):
@ -270,35 +287,11 @@ def _find_records(db, filter, callname):
return [(text, varname, locals()[varname]) for (text, varname, default) in RECORDS] return [(text, varname, locals()[varname]) for (text, varname, default) in RECORDS]
def _person_get_display_name(person, callname):
# Make a copy of the name object so we don't mess around with the real
# data.
n = Name(source=person.get_primary_name())
if n.call:
if callname == RecordsReportOptions.CALLNAME_REPLACE:
n.first_name = n.call
elif callname == RecordsReportOptions.CALLNAME_UNDERLINE_ADD:
if n.call in n.first_name:
(before, after) = n.first_name.split(n.call)
n.first_name = "%(before)s<u>%(call)s</u>%(after)s" % {
'before': before,
'call': n.call,
'after': after}
else:
n.first_name = "\"%(call)s\" (%(first)s)" % {
'call': n.call,
'first': n.first_name}
return name_displayer.display_name(n)
def _record(lowest, highest, value, text, handle_type, handle): def _record(lowest, highest, value, text, handle_type, handle):
if lowest is not None: if lowest is not None:
lowest.append((value, text, handle_type, handle)) lowest.append((value, text, handle_type, handle))
lowest.sort(lambda a,b: cmp(a[0], b[0])) lowest.sort(lambda a,b: cmp(a[0], b[0])) # FIXME: Ist das lambda notwendig?
for i in range(RecordsReportOptions.TOP_SIZE, len(lowest)): for i in range(RecordsReportOptions.TOP_SIZE, len(lowest)):
if lowest[i-1][0] < lowest[i][0]: if lowest[i-1][0] < lowest[i][0]:
del lowest[i:] del lowest[i:]
@ -317,6 +310,82 @@ def _output(value):
return str(value) return str(value)
#------------------------------------------------------------------------
#
# Reusable functions (could be methods of gen.lib.*)
#
#------------------------------------------------------------------------
_Name_CALLNAME_DONTUSE = 0
_Name_CALLNAME_REPLACE = 1
_Name_CALLNAME_UNDERLINE_ADD = 2
def _Name_get_styled(name, callname, placeholder=False):
"""
Return a StyledText object with the name formatted according to the
parameters:
@param callname: whether the callname should be used instead of the first
name (CALLNAME_REPLACE), underlined within the first name
(CALLNAME_UNDERLINE_ADD) or not used at all (CALLNAME_DONTUSE).
@param placeholder: whether a series of underscores should be inserted as a
placeholder if first name or surname are missing.
"""
# Make a copy of the name object so we don't mess around with the real
# data.
n = Name(source=name)
# Insert placeholders.
if placeholder:
if not n.first_name:
n.first_name = "____________"
if not n.surname:
n.surname = "____________"
if n.call:
if callname == _Name_CALLNAME_REPLACE:
# Replace first name with call name.
n.first_name = n.call
elif callname == _Name_CALLNAME_UNDERLINE_ADD:
if n.call not in n.first_name:
# Add call name to first name.
n.first_name = "\"%(call)s\" (%(first)s)" % {
'call': n.call,
'first': n.first_name}
text = name_displayer.display_name(n)
tags = []
if n.call:
if callname == _Name_CALLNAME_UNDERLINE_ADD:
# "name" in next line is on purpose: only underline the call name
# if it was a part of the *original* first name
if n.call in name.first_name:
# Underline call name
callpos = text.find(n.call)
tags = [StyledTextTag(StyledTextTagType.UNDERLINE, True,
[(callpos, callpos + len(n.call))])]
return StyledText(text, tags)
def _Person_get_styled_primary_name(person, callname, placeholder=False):
"""
Return a StyledText object with the person's name formatted according to
the parameters:
@param callname: whether the callname should be used instead of the first
name (CALLNAME_REPLACE), underlined within the first name
(CALLNAME_UNDERLINE_ADD) or not used at all (CALLNAME_DONTUSE).
@param placeholder: whether a series of underscores should be inserted as a
placeholder if first name or surname are missing.
"""
return _Name_get_styled(person.get_primary_name(), callname, placeholder)
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
# The Gramplet # The Gramplet
@ -329,6 +398,7 @@ class RecordsGramplet(Gramplet):
self.set_tooltip(_("Double-click name for details")) self.set_tooltip(_("Double-click name for details"))
self.set_text(_("No Family Tree loaded.")) self.set_text(_("No Family Tree loaded."))
def db_changed(self): def db_changed(self):
self.dbstate.db.connect('person-rebuild', self.update) self.dbstate.db.connect('person-rebuild', self.update)
self.dbstate.db.connect('family-rebuild', self.update) self.dbstate.db.connect('family-rebuild', self.update)
@ -336,8 +406,7 @@ class RecordsGramplet(Gramplet):
def main(self): def main(self):
self.set_text(_("Processing...") + "\n") self.set_text(_("Processing...") + "\n")
yield True yield True
records = _find_records(self.dbstate.db, None, records = _find_records(self.dbstate.db, None, _Name_CALLNAME_DONTUSE)
RecordsReportOptions.CALLNAME_DONTUSE)
self.set_text("") self.set_text("")
for (text, varname, top) in records: for (text, varname, top) in records:
yield True yield True
@ -349,7 +418,7 @@ class RecordsGramplet(Gramplet):
last_value = value last_value = value
rank = number rank = number
self.append_text("\n %s. " % (rank+1)) self.append_text("\n %s. " % (rank+1))
self.link(name, handletype, handle) self.link(str(name), handletype, handle)
self.append_text(" (%s)" % _output(value)) self.append_text(" (%s)" % _output(value))
self.append_text("\n") self.append_text("\n")
self.append_text("", scroll_to='begin') self.append_text("", scroll_to='begin')
@ -373,9 +442,12 @@ class RecordsReport(Report):
self.callname = menu.get_option_by_name('callname').get_value() self.callname = menu.get_option_by_name('callname').get_value()
self.include = dict([varname, self.footer = menu.get_option_by_name('footer').get_value()
menu.get_option_by_name(varname).get_value()]
for (_1, varname, _3) in RECORDS) self.include = {}
for (text, varname, default) in RECORDS:
self.include[varname] = menu.get_option_by_name(varname).get_value()
def write_report(self): def write_report(self):
""" """
@ -388,6 +460,10 @@ class RecordsReport(Report):
self.doc.write_text(_("Records")) self.doc.write_text(_("Records"))
self.doc.end_paragraph() self.doc.end_paragraph()
self.doc.start_paragraph('REC-Subtitle')
self.doc.write_text(self.filter.get_name())
self.doc.end_paragraph()
for (text, varname, top) in records: for (text, varname, top) in records:
if not self.include[varname]: if not self.include[varname]:
continue continue
@ -403,12 +479,15 @@ class RecordsReport(Report):
last_value = value last_value = value
rank = number rank = number
self.doc.start_paragraph('REC-Normal') self.doc.start_paragraph('REC-Normal')
self.doc.write_text(_("%(number)s. %(name)s (%(value)s)") % { self.doc.write_text(_("%(number)s. ") % {'number': rank+1})
'number': rank+1, self.doc.write_markup(str(name), name.get_tags())
'name': name, self.doc.write_text(_(" (%(value)s)") % {'value': _output(value)})
'value': _output(value)})
self.doc.end_paragraph() self.doc.end_paragraph()
self.doc.start_paragraph('REC-Footer')
self.doc.write_text(self.footer)
self.doc.end_paragraph()
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
@ -420,9 +499,6 @@ class RecordsReportOptions(MenuReportOptions):
Defines options and provides handling interface. Defines options and provides handling interface.
""" """
CALLNAME_DONTUSE = 0
CALLNAME_REPLACE = 1
CALLNAME_UNDERLINE_ADD = 2
REGULAR_DATES_ONLY = True REGULAR_DATES_ONLY = True
TOP_SIZE = 3 TOP_SIZE = 3
@ -451,13 +527,16 @@ class RecordsReportOptions(MenuReportOptions):
self.__update_filters() self.__update_filters()
callname = EnumeratedListOption(_("Use call name"), self.CALLNAME_DONTUSE) callname = EnumeratedListOption(_("Use call name"), _Name_CALLNAME_DONTUSE)
callname.set_items([ callname.set_items([
(self.CALLNAME_DONTUSE, _("Don't use call name")), (_Name_CALLNAME_DONTUSE, _("Don't use call name")),
(self.CALLNAME_REPLACE, _("Replace first name with call name")), (_Name_CALLNAME_REPLACE, _("Replace first name with call name")),
(self.CALLNAME_UNDERLINE_ADD, _("Underline call name in first name / add call name to first name"))]) (_Name_CALLNAME_UNDERLINE_ADD, _("Underline call name in first name / add call name to first name"))])
menu.add_option(category_name, "callname", callname) menu.add_option(category_name, "callname", callname)
footer = StringOption(_("Footer text"), "")
menu.add_option(category_name, "footer", footer)
for (text, varname, default) in RECORDS: for (text, varname, default) in RECORDS:
option = BooleanOption(text, default) option = BooleanOption(text, default)
if varname.startswith('person'): if varname.startswith('person'):
@ -496,34 +575,57 @@ class RecordsReportOptions(MenuReportOptions):
#Paragraph Styles #Paragraph Styles
font = FontStyle() font = FontStyle()
font.set_type_face(FONT_SANS_SERIF) font.set_type_face(FONT_SANS_SERIF)
font.set_size(10) font.set_size(16)
font.set_bold(0) font.set_bold(True)
para = ParagraphStyle() para = ParagraphStyle()
para.set_font(font) para.set_font(font)
para.set_description(_('The basic style used for the text display.')) para.set_alignment(PARA_ALIGN_CENTER)
default_style.add_paragraph_style('REC-Normal', para) para.set_description(_("The style used for the report title."))
default_style.add_paragraph_style('REC-Title', para)
font = FontStyle()
font.set_type_face(FONT_SANS_SERIF)
font.set_size(10)
font.set_bold(1)
para = ParagraphStyle()
para.set_font(font)
para.set_description(_('The style used for headings.'))
default_style.add_paragraph_style('REC-Heading', para)
font = FontStyle() font = FontStyle()
font.set_type_face(FONT_SANS_SERIF) font.set_type_face(FONT_SANS_SERIF)
font.set_size(12) font.set_size(12)
font.set_bold(1) font.set_bold(True)
para = ParagraphStyle() para = ParagraphStyle()
para.set_font(font) para.set_font(font)
para.set_description(_("The style used for the report title.")) para.set_alignment(PARA_ALIGN_CENTER)
default_style.add_paragraph_style('REC-Title', para) para.set_bottom_border(True)
para.set_bottom_margin(ReportUtils.pt2cm(8))
para.set_description(_("The style used for the report subtitle."))
default_style.add_paragraph_style('REC-Subtitle', para)
font = FontStyle()
font.set_size(12)
font.set_bold(True)
para = ParagraphStyle()
para.set_font(font)
para.set_top_margin(ReportUtils.pt2cm(6))
para.set_description(_('The style used for headings.'))
default_style.add_paragraph_style('REC-Heading', para)
font = FontStyle()
font.set_size(10)
para = ParagraphStyle()
para.set_font(font)
para.set_left_margin(0.5)
para.set_description(_('The basic style used for the text display.'))
default_style.add_paragraph_style('REC-Normal', para)
font = FontStyle()
font.set_size(8)
para = ParagraphStyle()
para.set_font(font)
para.set_alignment(PARA_ALIGN_CENTER)
para.set_top_border(True)
para.set_top_margin(ReportUtils.pt2cm(8))
para.set_description(_('The style used for the footer.'))
default_style.add_paragraph_style('REC-Footer', para)
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
# List of records (must be defined after declaration of _()) # List of records
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
RECORDS = [ RECORDS = [