Files
gramps/gramps/plugins/webreport/person.py
Serge Noiraud d89557acf4 Narrative web: some strings not translated (#838)
* Narrative web: some strings not translated

The confidence and the date are not translated in the family map page.
The date doesn't use the specified date format.

Fixes #11207

* Narrative web: another string to translate
2019-08-11 22:05:18 +02:00

1929 lines
80 KiB
Python

# -*- coding: utf-8 -*-
#!/usr/bin/env python
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
# Copyright (C) 2007 Johan Gonqvist <johan.gronqvist@gmail.com>
# Copyright (C) 2007-2009 Gary Burton <gary.burton@zen.co.uk>
# Copyright (C) 2007-2009 Stephane Charette <stephanecharette@gmail.com>
# Copyright (C) 2008-2009 Brian G. Matherly
# Copyright (C) 2008 Jason M. Simanek <jason@bohemianalps.com>
# Copyright (C) 2008-2011 Rob G. Healey <robhealey1@gmail.com>
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
# Copyright (C) 2010 Jakim Friant
# Copyright (C) 2010- Serge Noiraud
# Copyright (C) 2011 Tim G L Lyons
# Copyright (C) 2013 Benny Malengier
# Copyright (C) 2016 Allen Crider
# Copyright (C) 2018 Theo van Rijn
#
# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
"""
Narrative Web Page generator.
Classe:
PersonPage - Person index page and individual `Person pages
"""
#------------------------------------------------
# python modules
#------------------------------------------------
from collections import defaultdict
from operator import itemgetter
from decimal import Decimal, getcontext
import logging
#------------------------------------------------
# Gramps module
#------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
from gramps.gen.lib import (ChildRefType, Date, Name, Person, EventRoleType,
EventType)
from gramps.gen.lib.date import Today
from gramps.gen.plug.report import Bibliography
from gramps.gen.plug.report import utils
from gramps.gen.utils.alive import probably_alive
from gramps.gen.constfunc import win
from gramps.gen.display.name import displayer as _nd
from gramps.gen.utils.db import get_birth_or_fallback, get_death_or_fallback
from gramps.plugins.lib.libhtml import Html
from gramps.gen.utils.place import conv_lat_lon
from gramps.gen.proxy import LivingProxyDb
#------------------------------------------------
# specific narrative web import
#------------------------------------------------
from gramps.plugins.webreport.basepage import BasePage
from gramps.plugins.webreport.common import (get_first_letters, _KEYPERSON,
alphabet_navigation, sort_people,
_NAME_STYLE_FIRST, first_letter,
get_index_letter, add_birthdate,
primary_difference, FULLCLEAR,
_find_birth_date, _find_death_date,
MARKER_PATH, OSM_MARKERS,
GOOGLE_MAPS, MARKERS, html_escape,
DROPMASTERS, FAMILYLINKS)
from gramps.plugins.webreport.layout import LayoutTree
from gramps.plugins.webreport.buchheim import buchheim
_ = glocale.translation.sgettext
LOG = logging.getLogger(".NarrativeWeb")
getcontext().prec = 8
_WIDTH = 160
_HEIGHT = 120
_VGAP = 10
_HGAP = 30
_SHADOW = 5
_XOFFSET = 5
_YOFFSET = 5
_LOFFSET = 20
#################################################
#
# creates the Individual List Page and IndividualPages
#
#################################################
class PersonPages(BasePage):
"""
This class is responsible for displaying information about the 'Person'
database objects. It displays this information under the 'Individuals'
tab. It is told by the 'add_instances' call which 'Person's to display,
and remembers the list of persons. A single call to 'display_pages'
displays both the Individual List (Index) page and all the Individual
pages.
The base class 'BasePage' is initialised once for each page that is
displayed.
"""
def __init__(self, report):
"""
@param: report -- The instance of the main report class for this report
"""
BasePage.__init__(self, report, title="")
self.ind_dict = defaultdict(set)
self.mapservice = None
self.sort_name = None
self.googleopts = None
self.googlemapkey = None
self.birthorder = None
self.person = None
self.familymappages = None
self.rel_class = None
self.placemappages = None
self.name = None
def display_pages(self, title):
"""
Generate and output the pages under the Individuals tab, namely the
individual index and the individual pages.
@param: title -- Is the title of the web page
"""
LOG.debug("obj_dict[Person]")
for item in self.report.obj_dict[Person].items():
LOG.debug(" %s", str(item))
message = _('Creating individual pages')
with self.r_user.progress(_("Narrated Web Site Report"), message,
len(self.report.obj_dict[Person]) + 1
) as step:
index = 1
for person_handle in sorted(self.report.obj_dict[Person]):
step()
index += 1
person = self.r_db.get_person_from_handle(person_handle)
self.individualpage(self.report, title, person)
step()
self.individuallistpage(self.report, title,
self.report.obj_dict[Person].keys())
#################################################
#
# creates the Individual List Page
#
#################################################
def individuallistpage(self, report, title, ppl_handle_list):
"""
Creates an individual page
@param: report -- The instance of the main report class
for this report
@param: title -- Is the title of the web page
@param: ppl_handle_list -- The list of people for whom we need
to create a page.
"""
BasePage.__init__(self, report, title)
prev_letter = " "
# plugin variables for this module
showbirth = report.options['showbirth']
showdeath = report.options['showdeath']
showpartner = report.options['showpartner']
showparents = report.options['showparents']
output_file, sio = self.report.create_file("individuals")
result = self.write_header(self._("Individuals"))
indlistpage, dummy_head, dummy_body, outerwrapper = result
date = 0
# begin Individuals division
with Html("div", class_="content", id="Individuals") as individuallist:
outerwrapper += individuallist
# Individual List page message
msg = self._("This page contains an index of all the individuals "
"in the database, sorted by their last names. "
"Selecting the person&#8217;s "
"name will take you to that "
"person&#8217;s individual page.")
individuallist += Html("p", msg, id="description")
# add alphabet navigation
index_list = get_first_letters(self.r_db, ppl_handle_list,
_KEYPERSON, rlocale=self.rlocale)
alpha_nav = alphabet_navigation(index_list, self.rlocale)
if alpha_nav is not None:
individuallist += alpha_nav
# begin table and table head
with Html("table",
class_="infolist primobjlist IndividualList") as table:
individuallist += table
thead = Html("thead")
table += thead
trow = Html("tr")
thead += trow
# show surname and first name
trow += Html("th", self._("Surname"), class_="ColumnSurname",
inline=True)
trow += Html("th", self._("Given Name"), class_="ColumnName",
inline=True)
if showbirth:
trow += Html("th", self._("Birth"), class_="ColumnDate",
inline=True)
if showdeath:
trow += Html("th", self._("Death"), class_="ColumnDate",
inline=True)
if showpartner:
trow += Html("th", self._("Partner"),
class_="ColumnPartner",
inline=True)
if showparents:
trow += Html("th", self._("Parents"),
class_="ColumnParents",
inline=True)
tbody = Html("tbody")
table += tbody
ppl_handle_list = sort_people(self.r_db, ppl_handle_list,
self.rlocale)
first = True
name_format = self.report.options['name_format']
nme_format = _nd.name_formats[name_format][1]
for (surname, handle_list) in ppl_handle_list:
if surname and not surname.isspace():
letter = get_index_letter(first_letter(surname), index_list,
self.rlocale)
else:
letter = '&nbsp'
surname = self._("<absent>")
# In case the user choose a format name like "*SURNAME*"
# We must display this field in upper case. So we use the
# english format of format_name to find if this is the case.
# name_format = self.report.options['name_format']
# nme_format = _nd.name_formats[name_format][1]
if "SURNAME" in nme_format:
surnamed = surname.upper()
else:
surnamed = surname
first_surname = True
for person_handle in sorted(handle_list,
key=self.sort_on_name_and_grampsid):
person = self.r_db.get_person_from_handle(person_handle)
if person.get_change_time() > date:
date = person.get_change_time()
# surname column
trow = Html("tr")
tbody += trow
tcell = Html("td", class_="ColumnSurname", inline=True)
trow += tcell
if first or primary_difference(letter, prev_letter,
self.rlocale):
first = False
first_surname = False
prev_letter = letter
trow.attr = 'class = "BeginSurname"'
ttle = self._("Surnames %(surname)s beginning "
"with letter %(letter)s" %
{'surname' : surname,
'letter' : letter})
tcell += Html(
"a", html_escape(surnamed), name=letter,
id_=letter,
title=ttle)
elif first_surname:
first_surname = False
tcell += Html("a", html_escape(surnamed),
title=self._("Surnames") + " " + surname)
else:
tcell += "&nbsp;"
# firstname column
link = self.new_person_link(person_handle, person=person,
name_style=_NAME_STYLE_FIRST)
trow += Html("td", link, class_="ColumnName")
# birth column
if showbirth:
tcell = Html("td", class_="ColumnBirth", inline=True)
trow += tcell
birth_date = _find_birth_date(self.r_db, person)
if birth_date is not None:
if birth_date.fallback:
tcell += Html('em',
self.rlocale.get_date(birth_date),
inline=True)
else:
tcell += self.rlocale.get_date(birth_date)
else:
tcell += "&nbsp;"
# death column
if showdeath:
tcell = Html("td", class_="ColumnDeath", inline=True)
trow += tcell
death_date = _find_death_date(self.r_db, person)
if death_date is not None:
if death_date.fallback:
tcell += Html('em',
self.rlocale.get_date(death_date),
inline=True)
else:
tcell += self.rlocale.get_date(death_date)
else:
tcell += "&nbsp;"
# partner column
if showpartner:
family_list = person.get_family_handle_list()
first_family = True
#partner_name = None
tcell = ()
if family_list:
for family_handle in family_list:
family = self.r_db.get_family_from_handle(
family_handle)
partner_handle = utils.find_spouse(
person, family)
if partner_handle:
if not first_family:
# have to do this to get the comma on
# the same line as the link
if isinstance(tcell[-1], Html):
# tcell is an instance of Html (or
# of a subclass thereof)
tcell[-1].inside += ","
else:
tcell = tcell[:-1] + (
# TODO for Arabic, translate?
(tcell[-1] + ", "),)
# Have to manipulate as tuples so that
# subsequent people are not nested
# within the first link
tcell += (
self.new_person_link(partner_handle),)
first_family = False
else:
tcell = "&nbsp;"
trow += Html("td", class_="ColumnPartner") + tcell
# parents column
if showparents:
parent_hdl_list = person.get_parent_family_handle_list()
if parent_hdl_list:
parent_handle = parent_hdl_list[0]
family = self.r_db.get_family_from_handle(
parent_handle)
father_handle = family.get_father_handle()
mother_handle = family.get_mother_handle()
if father_handle:
father = self.r_db.get_person_from_handle(
father_handle)
else:
father = None
if mother_handle:
mother = self.r_db.get_person_from_handle(
mother_handle)
else:
mother = None
if father:
father_name = self.get_name(father)
if mother:
mother_name = self.get_name(mother)
samerow = False
if mother and father:
tcell = (Html("span", father_name,
class_="father fatherNmother",
inline=True),
Html("span", mother_name,
class_="mother", inline=True))
elif mother:
tcell = Html("span", mother_name,
class_="mother", inline=True)
elif father:
tcell = Html("span", father_name,
class_="father", inline=True)
else:
tcell = "&nbsp;"
samerow = True
else:
tcell = "&nbsp;"
samerow = True
trow += Html("td", class_="ColumnParents",
inline=samerow) + tcell
# create clear line for proper styling
# create footer section
footer = self.write_footer(date)
outerwrapper += (FULLCLEAR, footer)
# send page out for processing
# and close the file
self.xhtml_writer(indlistpage, output_file, sio, date)
#################################################
#
# creates an Individual Page
#
#################################################
gender_map = {
Person.MALE : _('male'),
Person.FEMALE : _('female'),
Person.UNKNOWN : _('unknown'),
}
def individualpage(self, report, title, person):
"""
Creates an individual page
@param: report -- The instance of the main report class for this report
@param: title -- Is the title of the web page
@param: person -- The person to use for this page.
"""
BasePage.__init__(self, report, title, person.get_gramps_id())
place_lat_long = []
self.person = person
self.bibli = Bibliography()
self.sort_name = self.get_name(person)
self.name = self.get_name(person)
date = self.person.get_change_time()
# to be used in the Family Map Pages...
self.familymappages = self.report.options['familymappages']
self.placemappages = self.report.options['placemappages']
self.mapservice = self.report.options['mapservice']
self.googleopts = self.report.options['googleopts']
self.googlemapkey = self.report.options['googlemapkey']
# decide if we will sort the birth order of siblings...
self.birthorder = self.report.options['birthorder']
# get the Relationship Calculator so that we can determine
# bio, half, step- siblings for use in display_ind_parents() ...
self.rel_class = self.report.rel_class
output_file, sio = self.report.create_file(person.get_handle(), "ppl")
self.uplink = True
result = self.write_header(self.sort_name)
indivdetpage, head, dummy_body, outerwrapper = result
# attach the ancestortree style sheet if ancestor
# graph is being created?
if self.report.options["ancestortree"]:
if self.usecms:
fname = "/".join([self.target_uri, "css", "ancestortree.css"])
else:
fname = "/".join(["css", "ancestortree.css"])
url = self.report.build_url_fname(fname, None, self.uplink)
head += Html("link", href=url, type="text/css", media="screen",
rel="stylesheet")
# begin individualdetail division
with Html("div", class_="content",
id='IndividualDetail') as individualdetail:
outerwrapper += individualdetail
# display a person's general data
thumbnail, name, summary = self.display_ind_general()
if thumbnail is not None:
individualdetail += thumbnail
individualdetail += (name, summary)
# display Narrative Notes
notelist = person.get_note_list()
sect8 = self.display_note_list(notelist)
if sect8 is not None:
individualdetail += sect8
# display a person's events
sect2 = self.display_ind_events(place_lat_long)
if sect2 is not None:
individualdetail += sect2
if self.report.options['relation']:
# display relationship to the center person
sect3 = self.display_ind_center_person()
if sect3 is not None:
individualdetail += sect3
# display parents
sect4 = self.display_ind_parents()
if sect4 is not None:
individualdetail += sect4
# display relationships
relationships = self.display_relationships(self.person,
place_lat_long)
if relationships is not None:
individualdetail += relationships
# display LDS ordinance
sect5 = self.display_lds_ordinance(self.person)
if sect5 is not None:
individualdetail += sect5
# display address(es) and show sources
sect6 = self.display_addr_list(self.person.get_address_list(), True)
if sect6 is not None:
individualdetail += sect6
photo_list = self.person.get_media_list()
media_list = photo_list[:]
# if Family Pages are not being created, then include the Family
# Media objects? There is no reason to add these objects to the
# Individual Pages...
if not self.inc_families:
for handle in self.person.get_family_handle_list():
family = self.r_db.get_family_from_handle(handle)
if family:
media_list += family.get_media_list()
for evt_ref in family.get_event_ref_list():
event = self.r_db.get_event_from_handle(evt_ref.ref)
media_list += event.get_media_list()
# if the Event Pages are not being created, then include the Event
# Media objects? There is no reason to add these objects to the
# Individual Pages...
if not self.inc_events:
for evt_ref in self.person.get_primary_event_ref_list():
event = self.r_db.get_event_from_handle(evt_ref.ref)
if event:
media_list += event.get_media_list()
# display additional images as gallery
sect7 = self.disp_add_img_as_gallery(media_list, person)
if sect7 is not None:
individualdetail += sect7
# display attributes
attrlist = person.get_attribute_list()
if attrlist:
attrsection, attrtable = self.display_attribute_header()
self.display_attr_list(attrlist, attrtable)
individualdetail += attrsection
# display web links
sect10 = self.display_url_list(self.person.get_url_list())
if sect10 is not None:
individualdetail += sect10
# display associations
assocs = person.get_person_ref_list()
if assocs:
individualdetail += self.display_ind_associations(assocs)
# for use in family map pages...
if place_lat_long:
if self.report.options["familymappages"]:
# save output_file, string_io and cur_fname
# before creating a new page
sof = output_file
sstring_io = sio
sfname = self.report.cur_fname
individualdetail += self.__display_family_map(
person, place_lat_long)
# restore output_file, string_io and cur_fname
# after creating a new page
output_file = sof
sio = sstring_io
self.report.cur_fname = sfname
# display pedigree
sect13 = self.display_ind_pedigree()
if sect13 is not None:
individualdetail += sect13
# display ancestor tree
if report.options['ancestortree']:
sect14 = self.display_tree()
if sect14 is not None:
individualdetail += sect14
# display source references
sect14 = self.display_ind_sources(person)
if sect14 is not None:
individualdetail += sect14
# add clearline for proper styling
# create footer section
footer = self.write_footer(date)
outerwrapper += (FULLCLEAR, footer)
# send page out for processing
# and close the file
self.xhtml_writer(indivdetpage, output_file, sio, date)
def __create_family_map(self, person, place_lat_long):
"""
creates individual family map page
@param: person -- person from database
@param: place_lat_long -- for use in Family Map Pages
"""
if not place_lat_long:
return
output_file, sio = self.report.create_file(person.get_handle(), "maps")
self.uplink = True
result = self.write_header(self._("Family Map"))
familymappage, head, body, outerwrapper = result
minx, maxx = Decimal("0.00000001"), Decimal("0.00000001")
miny, maxy = Decimal("0.00000001"), Decimal("0.00000001")
xwidth, yheight = [], []
midx_, midy_, dummy_spanx, spany = [None]*4
number_markers = len(place_lat_long)
if number_markers > 1:
for (latitude, longitude, placetitle, handle,
date, etype) in place_lat_long:
xwidth.append(latitude)
yheight.append(longitude)
xwidth.sort()
yheight.sort()
minx = xwidth[0] if xwidth[0] else minx
maxx = xwidth[-1] if xwidth[-1] else maxx
minx, maxx = Decimal(minx), Decimal(maxx)
midx_ = str(Decimal((minx + maxx) /2))
miny = yheight[0] if yheight[0] else miny
maxy = yheight[-1] if yheight[-1] else maxy
miny, maxy = Decimal(miny), Decimal(maxy)
midy_ = str(Decimal((miny + maxy) /2))
midx_, midy_ = conv_lat_lon(midx_, midy_, "D.D8")
# get the integer span of latitude and longitude
dummy_spanx = int(maxx - minx)
spany = int(maxy - miny)
# set zoom level based on span of Longitude?
tinyset = [value for value in (-3, -2, -1, 0, 1, 2, 3)]
smallset = [value for value in (-4, -5, -6, -7, 4, 5, 6, 7)]
middleset = [value for value in (-8, -9, -10, -11, 8, 9, 10, 11)]
largeset = [value for value in (-11, -12, -13, -14, -15, -16,
-17, 11, 12, 13, 14, 15, 16, 17)]
if spany in tinyset or spany in smallset:
zoomlevel = 6
elif spany in middleset:
zoomlevel = 5
elif spany in largeset:
zoomlevel = 4
else:
zoomlevel = 3
# 0 = latitude, 1 = longitude, 2 = place title,
# 3 = handle, and 4 = date, 5 = event type...
# being sorted by date, latitude, and longitude...
place_lat_long = sorted(place_lat_long, key=itemgetter(4, 0, 1))
# for all plugins
# if family_detail_page
# if active
# call_(report, up, head)
# add narrative-maps style sheet
if self.usecms:
fname = "/".join([self.target_uri, "css", "narrative-maps.css"])
else:
fname = "/".join(["css", "narrative-maps.css"])
url = self.report.build_url_fname(fname, None, self.uplink)
head += Html("link", href=url, type="text/css", media="screen",
rel="stylesheet")
# add MapService specific javascript code
if self.mapservice == "Google":
src_js = GOOGLE_MAPS + "api/js?sensor=false"
if self.googlemapkey:
src_js += "&key=" + self.googlemapkey
head += Html("script", type="text/javascript",
src=src_js, inline=True)
else:
url = self.secure_mode
url += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
"css/bootstrap.min.css")
head += Html("link", href=url, type="text/javascript",
rel="stylesheet")
src_js = self.secure_mode
src_js += "ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"
head += Html("script", type="text/javascript",
src=src_js, inline=True)
src_js = "https://openlayers.org/en/latest/build/ol.js"
head += Html("script", type="text/javascript",
src=src_js, inline=True)
url = "https://openlayers.org/en/latest/css/ol.css"
head += Html("link", href=url, type="text/javascript",
rel="stylesheet")
src_js = self.secure_mode
src_js += ("maxcdn.bootstrapcdn.com/bootstrap/3.3.7/"
"js/bootstrap.min.js")
head += Html("script", type="text/javascript",
src=src_js, inline=True)
if number_markers > 0:
tracelife = "["
seq_ = 1
for index in range(0, (number_markers - 1)):
(latitude, longitude, placetitle, handle, date,
etype) = place_lat_long[index]
# are we using Google?
if self.mapservice == "Google":
# are we creating Family Links?
if self.googleopts == "FamilyLinks":
tracelife += """
new google.maps.LatLng(%s, %s),""" % (latitude, longitude)
# are we creating Drop Markers or Markers?
elif self.googleopts in ["Drop", "Markers"]:
tracelife += """
['%s', %s, %s, %d],""" % (placetitle.replace("'", "\\'"), latitude,
longitude, seq_)
# are we using OpenStreetMap?
else:
tracelife += """
[%f, %f, \'%s\'],""" % (float(longitude), float(latitude),
placetitle.replace("'", "\\'"))
seq_ += 1
# FIXME: The last element in the place_lat_long list is treated
# specially, and the code above is apparently repeated so as to
# avoid a comma at the end, and get the right closing. This is very
# ugly.
(latitude, longitude, placetitle, handle, date,
etype) = place_lat_long[-1]
# are we using Google?
if self.mapservice == "Google":
# are we creating Family Links?
if self.googleopts == "FamilyLinks":
tracelife += """
new google.maps.LatLng(%s, %s)
];""" % (latitude, longitude)
# are we creating Drop Markers or Markers?
elif self.googleopts in ["Drop", "Markers"]:
tracelife += """
['%s', %s, %s, %d]
];""" % (placetitle.replace("'", "\\'"), latitude, longitude, seq_)
# are we using OpenStreetMap?
elif self.mapservice == "OpenStreetMap":
tracelife += """
[%f, %f, \'%s\']
];""" % (float(longitude), float(latitude), placetitle.replace("'", "\\'"))
# begin MapDetail division...
with Html("div", class_="content", id="FamilyMapDetail") as mapdetail:
outerwrapper += mapdetail
# add page title
mapdetail += Html("h3",
html_escape(self._("Tracking %s")
% self.get_name(person)),
inline=True)
# page description
msg = self._("This map page represents that person "
"and any descendants with all of their event/ places. "
"If you place your mouse over "
"the marker it will display the place name. "
"The markers and the Reference "
"list are sorted in date order (if any?). "
"Clicking on a place&#8217;s "
"name in the Reference section will take you "
"to that place&#8217;s page.")
mapdetail += Html("p", msg, id="description")
# this is the style element where the Map is held in the CSS...
with Html("div", id="map_canvas") as canvas:
mapdetail += canvas
# begin javascript inline code...
with Html("script", deter="deter",
style='width =100%; height =100%;',
type="text/javascript", indent=False) as jsc:
head += jsc
# Link to Gramps marker
fname = "/".join(['images', 'marker.png'])
marker_path = self.report.build_url_image("marker.png",
"images",
self.uplink)
jsc += MARKER_PATH % marker_path
# are we using Google?
if self.mapservice == "Google":
# are we creating Family Links?
if self.googleopts == "FamilyLinks":
if midy_ is None:
jsc += FAMILYLINKS % (tracelife, latitude,
longitude, int(10))
else:
jsc += FAMILYLINKS % (tracelife, midx_, midy_,
zoomlevel)
# are we creating Drop Markers?
elif self.googleopts == "Drop":
if midy_ is None:
jsc += DROPMASTERS % (tracelife, latitude,
longitude, int(10))
else:
jsc += DROPMASTERS % (tracelife, midx_, midy_,
zoomlevel)
# we are creating Markers only...
else:
if midy_ is None:
jsc += MARKERS % (tracelife, latitude,
longitude, int(10))
else:
jsc += MARKERS % (tracelife, midx_, midy_,
zoomlevel)
# we are using OpenStreetMap...
else:
if midy_ is None:
jsc += OSM_MARKERS % (tracelife,
longitude,
latitude, 10)
else:
jsc += OSM_MARKERS % (tracelife, midy_, midx_,
zoomlevel)
# if Google and Drop Markers are selected,
# then add "Drop Markers" button?
if self.mapservice == "Google" and self.googleopts == "Drop":
mapdetail += Html("button", _("Drop Markers"),
id="drop", onclick="drop()", inline=True)
# add div for popups.
with Html("div", id="popup", inline=True) as popup:
mapdetail += popup
# begin place reference section and its table...
with Html("div", class_="subsection", id="references") as section:
mapdetail += section
section += Html("h4", self._("References"), inline=True)
with Html("table", class_="infolist") as table:
section += table
thead = Html("thead")
table += thead
trow = Html("tr")
thead += trow
trow.extend(
Html("th", label, class_=colclass, inline=True)
for (label, colclass) in [
(_("Date"), "ColumnDate"),
(_("Place Title"), "ColumnPlace"),
(_("Event Type"), "ColumnType")
]
)
tbody = Html("tbody")
table += tbody
for (latitude, longitude, placetitle, handle, date,
etype) in place_lat_long:
trow = Html("tr")
tbody += trow
trow.extend(
Html("td", data, class_=colclass, inline=True)
for data, colclass in [
(self.rlocale.get_date(date), "ColumnDate"),
(self.place_link(handle, placetitle,
uplink=True),
"ColumnPlace"),
(self._(str(etype)), "ColumnType")
]
)
# add body id for this page...
body.attr = 'id ="FamilyMap" onload ="initialize()"'
# add clearline for proper styling
# add footer section
footer = self.write_footer(None)
outerwrapper += (FULLCLEAR, footer)
# send page out for processing
# and close the file
self.xhtml_writer(familymappage, output_file, sio, 0)
def __display_family_map(self, person, place_lat_long):
"""
Create the family map link
@param: person -- The person to set in the box
@param: place_lat_long -- The center of the box
"""
# create family map page
self.__create_family_map(person, place_lat_long)
# begin family map division plus section title
with Html("div", class_="subsection", id="familymap") as familymap:
familymap += Html("h4", self._("Family Map"), inline=True)
# add family map link
person_handle = person.get_handle()
url = self.report.build_url_fname_html(person_handle, "maps", True)
familymap += self.family_map_link(person_handle, url)
# return family map link to its caller
return familymap
def draw_box(self, node, col, person):
"""
Draw the box around the AncestorTree Individual name box...
@param: node -- The node defining the box location
@param: col -- The generation number
@param: person -- The person to set in the box
"""
xoff = _XOFFSET + node.coord_x
top = _YOFFSET + node.coord_y
sex = person.get_gender()
if sex == Person.MALE:
divclass = "male"
elif sex == Person.FEMALE:
divclass = "female"
else:
divclass = "unknown"
boxbg = Html("div", class_="boxbg %s AncCol%s" % (divclass, col),
style="top: %dpx; left: %dpx;" % (top, xoff+1)
)
person_name = self.get_name(person)
# This does not use [new_]person_link because the requirements are
# unique
result = self.report.obj_dict.get(Person).get(person.handle)
if result is None or result[0] == "":
# The person is not included in the webreport or there is no link
# to them
boxbg += Html("span", person_name, class_="unlinked", inline=True)
else:
thumbnail_url = None
if self.create_media and col < 5:
photolist = person.get_media_list()
if photolist:
photo_handle = photolist[0].get_reference_handle()
photo = self.r_db.get_media_from_handle(photo_handle)
mime_type = photo.get_mime_type()
if mime_type:
region = self.media_ref_region_to_object(photo_handle,
person)
if region:
# make a thumbnail of this region
newpath = self.copy_thumbnail(
photo_handle, photo, region)
# TODO. Check if build_url_fname can be used.
newpath = "/".join(['..']*3 + [newpath])
if win():
newpath = newpath.replace('\\', "/")
thumbnail_url = newpath
else:
(dummy_photo_url, thumbnail_url) = \
self.report.prepare_copy_media(photo)
thumbnail_url = "/".join(['..']*3 + [thumbnail_url])
if win():
thumbnail_url = thumbnail_url.replace('\\', "/")
url = self.report.build_url_fname_html(person.handle, "ppl", True)
birth = death = ""
bd_event = get_birth_or_fallback(self.r_db, person)
if bd_event:
birth = self.rlocale.get_date(bd_event.get_date_object())
dd_event = get_death_or_fallback(self.r_db, person)
if dd_event:
death = self.rlocale.get_date(dd_event.get_date_object())
if death == "":
death = "..."
value = person_name + "<br/>*", birth, "<br/>+", death
if thumbnail_url is None:
boxbg += Html("a", href=url, class_="noThumb") + value
else:
thumb = Html("span", class_="thumbnail") + (
Html("img", src=thumbnail_url, alt="Image: " + person_name))
boxbg += Html("a", href=url) + thumb + value
shadow = Html(
"div", class_="shadow", inline=True,
style="top: %dpx; left: %dpx;" % (top + _SHADOW, xoff + _SHADOW))
return [boxbg, shadow]
def extend_line(self, c_node, p_node):
"""
Draw a line 'half the distance out to the parents. connect_line()
will then draw the horizontal to the parent and the vertical connector
to this line.
@param c_node -- Child node to draw from
@param p_node -- Parent node to draw towards
"""
width = (p_node.coord_x - c_node.coord_x - _WIDTH + 1)/2
assert width > 0
coord_x0 = _XOFFSET + c_node.coord_x + _WIDTH
coord_y0 = c_node.coord_y + _LOFFSET + _VGAP/2
style = "top: %dpx; left: %dpx; width: %dpx"
bvline = Html("div", class_="bvline", inline=True,
style=style % (coord_y0, coord_x0, width))
gvline = Html("div", class_="gvline", inline=True,
style=style % (
coord_y0+_SHADOW, coord_x0, width+_SHADOW))
return [bvline, gvline]
def connect_line(self, coord_xc, coord_yc, coord_xp, coord_yp):
"""
Draw the line horizontally back from the parent towards the child and
then the vertical connecting this line to the line drawn towards us
from the child.
@param: coord_cx -- X coordinate for the child
@param: coord_yp -- Y coordinate for the child
@param: coord_xp -- X coordinate for the parent
@param: coord_yp -- Y coordinate for the parent
"""
coord_y = min(coord_yc, coord_yp)
# xh is the X co-ordinate half way between the two nodes.
# dx is the X gap between the two nodes, remembering that the
# the coordinates are for the LEFT of both nodes.
coord_xh = (coord_xp + _WIDTH + coord_xc)/2
width_x = (coord_xp - _WIDTH - coord_xc)/2
assert width_x >= 0
stylew = "top: %dpx; left: %dpx; width: %dpx;"
styleh = "top: %dpx; left: %dpx; height: %dpx;"
cnct_bv = Html("div", class_="bvline", inline=True,
style=stylew % (coord_yp, coord_xh, width_x))
cnct_gv = Html("div", class_="gvline", inline=True,
style=stylew % (coord_yp+_SHADOW,
coord_xh+_SHADOW,
width_x))
# Experience says that line heights need to be 1 longer than we
# expect. I suspect this is because HTML treats the lines as
# 'number of pixels starting at...' so to create a line between
# pixels 2 and 5 we need to light pixels 2, 3, 4, 5 - FOUR - and
# not 5 - 2 = 3.
cnct_bh = Html("div", class_="bhline", inline=True,
style=styleh % (coord_y, coord_xh,
abs(coord_yp-coord_yc)+1))
cnct_gh = Html("div", class_="gvline", inline=True,
style=styleh % (coord_y+_SHADOW,
coord_xh+_SHADOW,
abs(coord_yp-coord_yc)+1))
cnct_gv = ''
cnct_gh = ''
return [cnct_bv, cnct_gv, cnct_bh, cnct_gh]
def draw_connected_box(self, p_node, c_node, gen, person):
"""
@param: p_node -- Parent node to draw and connect from
@param: c_node -- Child node to connect towards
@param: gen -- Generation providing an HTML style hint
@param: handle -- Parent node handle
"""
coord_cx = _XOFFSET + c_node.coord_x
coord_cy = _YOFFSET + c_node.coord_y
coord_px = _XOFFSET+p_node.coord_x
coord_py = _YOFFSET+p_node.coord_y
box = []
if person is None:
return box
box = self.draw_box(p_node, gen, person)
box += self.connect_line(
coord_cx, coord_cy+_LOFFSET, coord_px, coord_py+_LOFFSET)
return box
def create_layout_tree(self, p_handle, generations):
"""
Create a family subtree in a format that is suitable to pass to
the Buchheim algorithm.
@param: p_handle -- Handle for person at root of this subtree
@param: generation -- Generations left to add to tree.
"""
family_tree = None
if generations:
if p_handle:
person = self.r_db.get_person_from_handle(p_handle)
if person is None:
return None
family_handle = person.get_main_parents_family_handle()
f_layout_tree = None
m_layout_tree = None
if family_handle:
family = self.r_db.get_family_from_handle(family_handle)
if family is not None:
f_handle = family.get_father_handle()
m_handle = family.get_mother_handle()
f_layout_tree = self.create_layout_tree(
f_handle, generations-1)
m_layout_tree = self.create_layout_tree(
m_handle, generations-1)
family_tree = LayoutTree(
p_handle, f_layout_tree, m_layout_tree)
return family_tree
def display_tree(self):
"""
Display the Ancestor tree using a Buchheim tree.
Reference: Improving Walker's Algorithm to Run in Linear time
Christoph Buccheim, Michael Junger, Sebastian Leipert
This is more complex than a simple binary tree but it results in a much
more compact, but still sensible, layout which is especially good where
the tree has gaps that would otherwise result in large blank areas.
"""
family_handle = self.person.get_main_parents_family_handle()
if not family_handle:
return None
generations = self.report.options['graphgens']
# Begin by building a representation of the Ancestry tree that can be
# fed to the Buchheim algorithm. Note that the algorithm doesn't care
# who is the father and who is the mother.
#
# This routine is also about to go recursive!
layout_tree = self.create_layout_tree(
self.person.get_handle(), generations)
# We now apply the Buchheim algorith to this tree, and it assigns X
# and Y positions to all elements in the tree.
l_tree = buchheim(layout_tree, _WIDTH, _HGAP, _HEIGHT, _VGAP)
# We know the height in 'pixels' where every Ancestor will sit
# precisely on an integer unit boundary.
with Html("div", id="tree", class_="subsection") as tree:
tree += Html("h4", _('Ancestors'), inline=True)
with Html("div", id="treeContainer",
style="width:%dpx; height:%dpx;" % (
l_tree.width + _XOFFSET + _WIDTH,
l_tree.height + _HEIGHT + _VGAP)
) as container:
tree += container
container += self.draw_tree(l_tree, 1, None)
return tree
def draw_tree(self, l_node, gen_nr, c_node):
"""
Draws the Ancestor Tree
@param: l_node -- The tree node to draw
@param: gen_nr -- The generation number to draw
@param: c_node -- Child node of this parent
"""
tree = []
person = self.r_db.get_person_from_handle(l_node.handle())
if person is None:
return None
if gen_nr == 1:
tree = self.draw_box(l_node, 0, person)
else:
tree = self.draw_connected_box(
l_node, c_node, gen_nr-1, person)
# If there are any parents, we need to draw the extend line. We only
# use the parent to define the end of the line so either will do and
# we know we have at least one of this test passes.
if l_node.children:
tree += self.extend_line(l_node, l_node.children[0])
# The parents are equivalent and the drawing routine figures out
# whether they are male or female.
for p_node in l_node.children:
tree += self.draw_tree(p_node, gen_nr+1, l_node)
return tree
def display_ind_associations(self, assoclist):
"""
Display an individual's associations
@param: assoclist -- The list of persons for association
"""
# begin Associations division
with Html("div", class_="subsection", id="Associations") as section:
section += Html("h4", self._('Associations'), inline=True)
with Html("table", class_="infolist assoclist") as table:
section += table
thead = Html("thead")
table += thead
trow = Html("tr")
thead += trow
assoc_row = [
(self._("Person"), 'Person'),
(self._('Relationship'), 'Relationship'),
(self._("Notes"), 'Notes'),
(self._("Sources"), 'Sources'),
]
trow.extend(
Html("th", label, class_="Column" + colclass, inline=True)
for (label, colclass) in assoc_row)
tbody = Html("tbody")
table += tbody
for person_ref in assoclist:
trow = Html("tr")
tbody += trow
person_lnk = self.new_person_link(person_ref.ref,
uplink=True)
index = 0
for data in [
person_lnk,
person_ref.get_relation(),
self.dump_notes(person_ref.get_note_list()),
self.get_citation_links(
person_ref.get_citation_list()),
]:
# get colclass from assoc_row
colclass = assoc_row[index][1]
trow += Html("td", data, class_="Column" + colclass,
inline=True)
index += 1
# return section to its callers
return section
def display_ind_pedigree(self):
"""
Display an individual's pedigree
"""
birthorder = self.report.options["birthorder"]
# Define helper functions
def children_ped(ol_html):
"""
Create a children list
@param: ol_html -- The html element to complete
"""
if family:
childlist = family.get_child_ref_list()
childlist = [child_ref.ref for child_ref in childlist]
children = add_birthdate(self.r_db, childlist, self.rlocale)
if birthorder:
children = sorted(children)
for dummy_birthdate, dummy_birth, \
dummy_death, handle in children:
if handle == self.person.get_handle():
child_ped(ol_html)
elif handle:
child = self.r_db.get_person_from_handle(handle)
if child:
ol_html += Html("li") + self.pedigree_person(child)
else:
child_ped(ol_html)
return ol_html
def child_ped(ol_html):
"""
Create a child element list
@param: ol_html -- The html element to complete
"""
with Html("li", self.name, class_="thisperson") as pedfam:
family = self.pedigree_family()
if family:
pedfam += Html("ol", class_="spouselist") + family
return ol_html + pedfam
# End of helper functions
parent_handle_list = self.person.get_parent_family_handle_list()
if parent_handle_list:
parent_handle = parent_handle_list[0]
family = self.r_db.get_family_from_handle(parent_handle)
father_handle = family.get_father_handle()
mother_handle = family.get_mother_handle()
if mother_handle:
mother = self.r_db.get_person_from_handle(mother_handle)
else:
mother = None
if father_handle:
father = self.r_db.get_person_from_handle(father_handle)
else:
father = None
else:
family = None
father = None
mother = None
with Html("div", id="pedigree", class_="subsection") as ped:
ped += Html("h4", self._('Pedigree'), inline=True)
with Html("ol", class_="pedigreegen") as pedol:
ped += pedol
if father and mother:
pedfa = Html("li") + self.pedigree_person(father)
pedol += pedfa
with Html("ol") as pedma:
pedfa += pedma
pedma += (Html("li", class_="spouse") +
self.pedigree_person(mother) +
children_ped(Html("ol"))
)
elif father:
pedol += (Html("li") + self.pedigree_person(father) +
children_ped(Html("ol"))
)
elif mother:
pedol += (Html("li") + self.pedigree_person(mother) +
children_ped(Html("ol"))
)
else:
pedol += (Html("li") + children_ped(Html("ol")))
return ped
def display_ind_general(self):
"""
display an individual's general information...
"""
self.page_title = self.sort_name
thumbnail = self.disp_first_img_as_thumbnail(
self.person.get_media_list(), self.person)
section_title = Html("h3", html_escape(self.page_title),
inline=True) + (
Html('sup') + (
Html('small') +
self.get_citation_links(
self.person.get_citation_list())))
# begin summaryarea division
with Html("div", id='summaryarea') as summaryarea:
# begin general details table
with Html("table", class_="infolist") as table:
summaryarea += table
primary_name = self.person.get_primary_name()
all_names = [primary_name] + self.person.get_alternate_names()
# if the callname or the nickname is the same as the 'first
# name' (given name), then they are not displayed.
first_name = primary_name.get_first_name()
# Names [and their sources]
for name in all_names:
pname = html_escape(_nd.display_name(name))
pname += self.get_citation_links(name.get_citation_list())
# if we have just a firstname, then the name is preceeded
# by ", " which doesn't exactly look very nice printed on
# the web page
if pname[:2] == ', ': # TODO for Arabic, translate this?
pname = pname[2:]
if name != primary_name:
datetext = self.rlocale.get_date(name.date)
if datetext:
pname = datetext + ': ' + pname
type_ = self._(name.get_type().xml_str())
trow = Html("tr") + (
Html("td", type_, class_="ColumnAttribute",
inline=True)
)
tcell = Html("td", pname, class_="ColumnValue")
# display any notes associated with this name
notelist = name.get_note_list()
if notelist:
unordered = Html("ul")
for notehandle in notelist:
note = self.r_db.get_note_from_handle(notehandle)
if note:
note_text = self.get_note_format(note, True)
# attach note
unordered += note_text
tcell += unordered
trow += tcell
table += trow
# display the callname associated with this name.
call_name = name.get_call_name()
if call_name and call_name != first_name:
trow = Html("tr") + (
Html("td", _("Call Name"), class_="ColumnAttribute",
inline=True),
Html("td", call_name, class_="ColumnValue",
inline=True)
)
table += trow
# display the nickname associated with this name. Note that
# this no longer displays the Nickname attribute (if
# present), because the nickname attribute is deprecated in
# favour of the nick_name property of the name structure
# (see http://gramps.1791082.n4.nabble.com/Where-is-
# nickname-stored-tp4469779p4484272.html), and also because
# the attribute is (normally) displayed lower down the
# wNarrative Web report.
nick_name = name.get_nick_name()
if nick_name and nick_name != first_name:
trow = Html("tr") + (
Html("td", self._("Nick Name"),
class_="ColumnAttribute",
inline=True),
Html("td", nick_name, class_="ColumnValue",
inline=True)
)
table += trow
# Gramps ID
person_gid = self.person.get_gramps_id()
if not self.noid and person_gid:
trow = Html("tr") + (
Html("td", self._("Gramps ID"),
class_="ColumnAttribute",
inline=True),
Html("td", person_gid, class_="ColumnValue",
inline=True)
)
table += trow
# Gender
gender = self._(self.gender_map[self.person.gender])
trow = Html("tr") + (
Html("td", self._("Gender"), class_="ColumnAttribute",
inline=True),
Html("td", gender, class_="ColumnValue", inline=True)
)
table += trow
# Age At Death???
birth_date = Date.EMPTY
birth_ref = self.person.get_birth_ref()
if birth_ref:
birth = self.r_db.get_event_from_handle(birth_ref.ref)
if birth:
birth_date = birth.get_date_object()
if birth_date and birth_date is not Date.EMPTY:
alive = probably_alive(self.person, self.r_db, Today())
death_date = _find_death_date(self.r_db, self.person)
if not alive and death_date is not None:
nyears = death_date - birth_date
nyears = nyears.format(precision=3,
dlocale=self.rlocale)
trow = Html("tr") + (
Html("td", self._("Age at Death"),
class_="ColumnAttribute", inline=True),
Html("td", nyears,
class_="ColumnValue", inline=True)
)
table += trow
# return all three pieces to its caller
# do NOT combine before returning
return thumbnail, section_title, summaryarea
def display_ind_events(self, place_lat_long):
"""
will create the events table
@param: place_lat_long -- For use in Family Map Pages. This will be None
if called from Family pages, which do not
create a Family Map
"""
event_ref_list = self.person.get_event_ref_list()
if not event_ref_list:
return None
# begin events division and section title
with Html("div", id="events", class_="subsection") as section:
section += Html("h4", self._("Events"), inline=True)
# begin events table
with Html("table", class_="infolist eventlist") as table:
section += table
thead = Html("thead")
table += thead
# attach event header row
thead += self.event_header_row()
tbody = Html("tbody")
table += tbody
for evt_ref in event_ref_list:
event = self.r_db.get_event_from_handle(evt_ref.ref)
if event:
# display event row
tbody += self.display_event_row(event, evt_ref,
place_lat_long,
True, True,
EventRoleType.PRIMARY)
return section
def display_parent(self, handle, title, rel):
"""
This will display a parent ...
@param: handle -- The person handle
@param: title -- Is the title of the web page
@param: rel -- The relation
"""
tcell1 = Html("td", title, class_="ColumnAttribute", inline=True)
tcell2 = Html("td", class_="ColumnValue", close=False, inline=True)
tcell2 += self.new_person_link(handle, uplink=True)
if rel and rel != ChildRefType(ChildRefType.BIRTH):
tcell2 += ''.join(['&nbsp;'] *3 + ['(%s)']) % str(rel)
person = self.r_db.get_person_from_handle(handle)
birth = death = ""
if person:
bd_event = get_birth_or_fallback(self.r_db, person)
if bd_event:
birth = self.rlocale.get_date(bd_event.get_date_object())
dd_event = get_death_or_fallback(self.r_db, person)
if dd_event:
death = self.rlocale.get_date(dd_event.get_date_object())
tcell3 = Html("td", birth, class_="ColumnDate",
inline=False, close=False, indent=False)
tcell4 = Html("td", death, class_="ColumnDate",
inline=True, close=False, indent=False)
tcell2 += tcell3
tcell2 += tcell4
# return table columns to its caller
return tcell1, tcell2
def get_reln_in_family(self, ind, family):
"""
Display the relation of the indiv in the family
@param: ind -- The person to use
@param: family -- The family
"""
child_handle = ind.get_handle()
child_ref_list = family.get_child_ref_list()
for child_ref in child_ref_list:
if child_ref.ref == child_handle:
return (child_ref.get_father_relation(),
child_ref.get_mother_relation())
return (None, None)
def display_ind_parent_family(
self, birthmother, birthfather, family, table, first=False):
"""
Display the individual parent family
@param: birthmother -- The birth mother
@param: birthfather -- The birth father
@param: family -- The family
@param: table -- The html document to complete
@param: first -- Is this the first indiv ?
"""
if not first:
trow = Html("tr") + (Html("td", "&nbsp;", colspan=3,
inline=True))
table += trow
# get the father
father_handle = family.get_father_handle()
if father_handle:
if father_handle == birthfather:
# The parent may not be birth father in ths family, because it
# may be a step family. However, it will be odd to display the
# parent as anything other than "Father"
reln = self._("Father")
else:
if self.step_or_not(family, father_handle):
reln = self._("Stepfather")
else:
reln = ""
trow = Html("tr") + (self.display_parent(father_handle, reln, None))
table += trow
# get the mother
mother_handle = family.get_mother_handle()
if mother_handle:
if mother_handle == birthmother:
reln = self._("Mother")
else:
if self.step_or_not(family, mother_handle):
reln = self._("Stepmother")
else:
reln = ""
trow = Html("tr") + (self.display_parent(mother_handle, reln, None))
table += trow
for child_ref in family.get_child_ref_list():
child_handle = child_ref.ref
child = self.r_db.get_person_from_handle(child_handle)
if child:
if child == self.person:
reln = ""
else:
try:
# We have a try except block here, because the two
# people MUST be siblings for the called Relationship
# routines to work. Depending on your definition of
# sibling, we cannot necessarily guarantee that.
sibling_type = self.rel_class.get_sibling_type(
self.r_db, self.person, child)
reln = self.rel_class.get_sibling_relationship_string(
sibling_type, self.person.gender, child.gender)
# We have a problem here : reln is never in the choosen
# language but in the default language.
# Does get_sibling_relationship_string work ?
reln = reln[0].upper() + reln[1:]
except Exception:
reln = self._("Not siblings")
val1 = "&nbsp;&nbsp;&nbsp;&nbsp;"
reln = val1 + reln
# Now output reln, child_link, (frel, mrel)
frel = child_ref.get_father_relation()
mrel = child_ref.get_mother_relation()
if frel != ChildRefType.BIRTH or mrel != ChildRefType.BIRTH:
frelmrel = "(%s, %s)" % (str(frel), str(mrel))
else:
frelmrel = ""
trow = Html("tr") + (
Html("td", reln, class_="ColumnAttribute", inline=True))
tcell = Html("td", val1, class_="ColumnValue", inline=True)
if child == self.person:
name_format = self.report.options['name_format']
primary_name = child.get_primary_name()
name = Name(primary_name)
name.set_display_as(name_format)
ndf = html_escape(_nd.display_name(name))
tcell += Html("b", ndf)
else:
tcell += self.display_child_link(child_handle)
birth = death = ""
bd_event = get_birth_or_fallback(self.r_db, child)
if bd_event:
birth = self.rlocale.get_date(bd_event.get_date_object())
dd_event = get_death_or_fallback(self.r_db, child)
if dd_event:
death = self.rlocale.get_date(dd_event.get_date_object())
tcell2 = Html("td", birth, class_="ColumnDate",
inline=True)
tcell3 = Html("td", death, class_="ColumnDate",
inline=True)
trow += tcell
trow += tcell2
trow += tcell3
tcell = Html("td", frelmrel, class_="ColumnValue",
inline=True)
trow += tcell
table += trow
def step_or_not(self, family, handle):
"""
Quickly check to see if this person is a stepmother or stepfather
We assume this is not a stepmother or a stepfather if :
1 - The father or mother died before the child birth
2 - The father or the mother divorced before the child birth
3 - The father or the mother married after the child death
In all other cases, they are stepfather or stepmother.
@param: family The family we examine
@param: handle The handle of the father or the mother
self.person The child for whom we need this result
"""
bd_date = Today()
bd_event = get_birth_or_fallback(self.r_db, self.person)
if bd_event:
bd_date = bd_event.get_date_object()
for event_ref in family.get_event_ref_list():
event = self.r_db.get_event_from_handle(event_ref.ref)
if (event.type == EventType.DIVORCE and
event_ref.get_role() in (EventRoleType.FAMILY,
EventRoleType.PRIMARY)):
dv_date = event.get_date_object()
if bd_date > dv_date:
# We have a divorce before the child birth
return False
if (event.type == EventType.MARRIAGE and
event_ref.get_role() in (EventRoleType.FAMILY,
EventRoleType.PRIMARY)):
dm_date = event.get_date_object()
dd_date = Today()
dd_event = get_death_or_fallback(self.r_db, self.person)
if dd_event:
dd_date = dd_event.get_date_object()
if dd_date < dm_date:
# We have a child death before the marriage
return False
pers = self.r_db.get_person_from_handle(handle)
death_date = Today()
death_event = get_death_or_fallback(self.r_db, pers)
if death_event:
death_date = death_event.get_date_object()
if bd_date > death_date:
# We have a death before the child birth
return False
return True
def display_step_families(self, parent_handle,
all_family_handles,
birthmother, birthfather,
table):
"""
Display step families
@param: parent_handle -- The family parent handle to display
@param: all_family_handles -- All known family handles
@param: birthmother -- The birth mother
@param: birthfather -- The birth father
@param: table -- The html document to complete
"""
if parent_handle:
parent = self.r_db.get_person_from_handle(parent_handle)
for parent_family_handle in parent.get_family_handle_list():
if parent_family_handle not in all_family_handles:
parent_family = self.r_db.get_family_from_handle(
parent_family_handle)
self.display_ind_parent_family(birthmother, birthfather,
parent_family, table)
all_family_handles.append(parent_family_handle)
return
def display_ind_center_person(self):
"""
Display the person's relationship to the center person
"""
center_person = self.r_db.get_person_from_gramps_id(
self.report.options['pid'])
if center_person is None:
return None
if (int(self.report.options['living_people']) !=
LivingProxyDb.MODE_INCLUDE_ALL):
if probably_alive(center_person, self.r_db, Today()):
return None
relationship = self.rel_class.get_one_relationship(self.r_db,
center_person,
self.person)
if relationship == "": # No relation to display
return None
# begin center_person division
section = ""
with Html("div", class_="subsection", id="parents") as section:
message = self._("Relation to the center person")
message += " ("
name_format = self.report.options['name_format']
primary_name = center_person.get_primary_name()
name = Name(primary_name)
name.set_display_as(name_format)
message += _nd.display_name(name)
message += ") : "
message += relationship
section += Html("h4", message, inline=True)
return section
def display_ind_parents(self):
"""
Display a person's parents
"""
parent_list = self.person.get_parent_family_handle_list()
if not parent_list:
return None
# begin parents division
with Html("div", class_="subsection", id="parents") as section:
section += Html("h4", self._("Parents"), inline=True)
# begin parents table
with Html("table", class_="infolist") as table:
section += table
thead = Html("thead")
table += thead
trow = Html("tr")
thead += trow
trow.extend(
Html("th", label, class_=colclass, inline=True)
for (label, colclass) in [
(self._("Relation to main person"), "ColumnAttribute"),
(self._("Name"), "ColumnValue"),
(self._("Birth date"), "ColumnValue"),
(self._("Death date"), "ColumnValue"),
(self._("Relation within this family "
"(if not by birth)"),
"ColumnValue")
]
)
tbody = Html("tbody")
all_family_handles = list(parent_list)
(birthmother, birthfather) = self.rel_class.get_birth_parents(
self.r_db, self.person)
first = True
for family_handle in parent_list:
family = self.r_db.get_family_from_handle(family_handle)
if family:
# Display this family
self.display_ind_parent_family(birthmother,
birthfather,
family, tbody, first)
first = False
if self.report.options['showhalfsiblings']:
# Display all families in which the parents are
# involved. This displays half siblings and step
# siblings
self.display_step_families(
family.get_father_handle(),
all_family_handles,
birthmother, birthfather, tbody)
self.display_step_families(
family.get_mother_handle(),
all_family_handles,
birthmother, birthfather, tbody)
table += tbody
return section
def pedigree_person(self, person):
"""
will produce a hyperlink for a pedigree person ...
@param: person -- The person
"""
hyper = self.new_person_link(person.handle, person=person, uplink=True)
return hyper
def pedigree_family(self):
"""
Returns a family pedigree
"""
ped = []
for family_handle in self.person.get_family_handle_list():
rel_family = self.r_db.get_family_from_handle(family_handle)
spouse_handle = utils.find_spouse(self.person, rel_family)
if spouse_handle:
spouse = self.r_db.get_person_from_handle(spouse_handle)
pedsp = (Html("li", class_="spouse") +
self.pedigree_person(spouse)
)
else:
pedsp = (Html("li", class_="spouse"))
ped += [pedsp]
childlist = rel_family.get_child_ref_list()
if childlist:
with Html("ol") as childol:
pedsp += [childol]
for child_ref in childlist:
child = self.r_db.get_person_from_handle(child_ref.ref)
if child:
childol += (Html("li") +
self.pedigree_person(child)
)
return ped