diff --git a/data/css/narrative-maps.css b/data/css/narrative-maps.css
index ab52d67f5..5c0682557 100644
--- a/data/css/narrative-maps.css
+++ b/data/css/narrative-maps.css
@@ -31,7 +31,7 @@ body#FamilyMap {
margin-left: 10px;
margin-right: 10px;
border: solid 4px #000;
- margin: 0px auto;
+ margin: 20px auto;
width: 800px;
height: 400px;
max-width: 90%;
diff --git a/gramps/plugins/webreport/basepage.py b/gramps/plugins/webreport/basepage.py
index 123c08309..a5b8dbb5f 100644
--- a/gramps/plugins/webreport/basepage.py
+++ b/gramps/plugins/webreport/basepage.py
@@ -2984,7 +2984,7 @@ class BasePage: # pylint: disable=C1001
gid = self.report.obj_dict[bkref_class][bkref_handle][2]
if role != "":
if self.reference_sort:
- role = ""
+ role = self.birth_death_dates(gid)
elif role[1:2] == ':':
# cal is the original calendar
cal, role = role.split(':')
@@ -3012,7 +3012,10 @@ class BasePage: # pylint: disable=C1001
# reset the date to the original calendar
cdate = date.to_calendar(Date.calendar_names[int(cal)])
ldate = self.rlocale.get_date(cdate)
- role = " (%s) " % ldate
+ evtype = self.event_for_date(gid, cdate)
+ if evtype:
+ evtype = " " + evtype
+ role = " (%s) " % (ldate + evtype)
else:
role = " (%s) " % self._(role)
ordered += list_html
@@ -3031,6 +3034,45 @@ class BasePage: # pylint: disable=C1001
list_html += Html("a", href=url) + name + role + gid_html
return ordered
+ def event_for_date(self, gid, date):
+ """
+ return the event type
+ """
+ pers = self.r_db.get_person_from_gramps_id(gid)
+ if pers:
+ evt_ref_list = pers.get_event_ref_list()
+ if evt_ref_list:
+ for evt_ref in evt_ref_list:
+ evt = self.r_db.get_event_from_handle(evt_ref.ref)
+ if evt:
+ evdate = evt.get_date_object()
+ # convert date to gregorian
+ _date = str(evdate.to_calendar("gregorian"))
+ if _date == str(date):
+ return self._(str(evt.get_type()))
+ return ""
+
+ def birth_death_dates(self, gid):
+ """
+ return the birth and death date for the person
+ """
+ pers = self.r_db.get_person_from_gramps_id(gid)
+ if pers:
+ birth = death = ""
+ evt_birth = get_birth_or_fallback(self.r_db, pers)
+ if evt_birth:
+ birthd = evt_birth.get_date_object()
+ # convert date to gregorian to avoid strange years
+ birth = str(birthd.to_calendar("gregorian").get_year())
+ evt_death = get_death_or_fallback(self.r_db, pers)
+ if evt_death:
+ deathd = evt_death.get_date_object()
+ # convert date to gregorian to avoid strange years
+ death = str(deathd.to_calendar("gregorian").get_year())
+ return "(%s-%s)" % (birth, death)
+ else:
+ return ""
+
def display_bkref_list(self, obj_class, obj_handle):
"""
Display a reference list for an object class
diff --git a/gramps/plugins/webreport/common.py b/gramps/plugins/webreport/common.py
index e000b54f6..2ac33490c 100644
--- a/gramps/plugins/webreport/common.py
+++ b/gramps/plugins/webreport/common.py
@@ -31,6 +31,7 @@ from unicodedata import normalize
from collections import defaultdict
from hashlib import md5
import re
+import locale # Used only with pyICU
import logging
from xml.sax.saxutils import escape
@@ -439,7 +440,10 @@ def sort_places(dbase, handle_list, rlocale=glocale):
for name in temp_list:
if isinstance(name, bytes):
name = name.decode('utf-8')
- sorted_lists.append((name, pname_sub[name][0]))
+ slist = sorted(((sortnames[x], x) for x in pname_sub[name]),
+ key=lambda x: rlocale.sort_key(x[0]))
+ for entry in slist:
+ sorted_lists.append(entry)
return sorted_lists
@@ -622,23 +626,42 @@ def first_letter(string, rlocale=glocale):
# no special case
return norm_unicode[0].upper()
-def primary_difference(prev_key, new_key, rlocale=glocale):
- """
- The PyICU collation doesn't work if you want to sort in another language
- So we use this method to do the work correctly.
- Returns true if there is a primary difference between the two parameters
- See http://www.gramps-project.org/bugs/view.php?id=2933#c9317 if
- letter[i]+'a' < letter[i+1]+'b' and letter[i+1]+'a' < letter[i]+'b' is
- true then the letters should be grouped together
+try:
+ import PyICU # pylint : disable=wrong-import-position
+ PRIM_COLL = PyICU.Collator.createInstance(PyICU.Locale(COLLATE_LANG))
+ PRIM_COLL.setStrength(PRIM_COLL.PRIMARY)
- The test characters here must not be any that are used in contractions.
- """
+ def primary_difference(prev_key, new_key, rlocale=glocale):
+ """
+ Try to use the PyICU collation.
+ If we generate a report for another language, make sure we use the good
+ collation sequence
+ """
+ collation = PRIM_COLL
+ if rlocale.lang != locale.getlocale(locale.LC_COLLATE)[0]:
+ encoding = rlocale.encoding if rlocale.encoding else "UTF-8"
+ collate_lang = PyICU.Locale(rlocale.collation+"."+encoding)
+ collation = PyICU.Collator.createInstance(collate_lang)
+ return collation.compare(prev_key, new_key) != 0
- return rlocale.sort_key(prev_key + "e") >= \
- rlocale.sort_key(new_key + "f") or \
- rlocale.sort_key(new_key + "e") >= \
- rlocale.sort_key(prev_key + "f")
+except:
+ def primary_difference(prev_key, new_key, rlocale=glocale):
+ """
+ The PyICU collation is not available.
+
+ Returns true if there is a primary difference between the two parameters
+ See http://www.gramps-project.org/bugs/view.php?id=2933#c9317 if
+ letter[i]+'a' < letter[i+1]+'b' and letter[i+1]+'a' < letter[i]+'b' is
+ true then the letters should be grouped together
+
+ The test characters here must not be any that are used in contractions.
+ """
+
+ return rlocale.sort_key(prev_key + "e") >= \
+ rlocale.sort_key(new_key + "f") or \
+ rlocale.sort_key(new_key + "e") >= \
+ rlocale.sort_key(prev_key + "f")
def get_first_letters(dbase, handle_list, key, rlocale=glocale):
"""
diff --git a/gramps/plugins/webreport/narrativeweb.py b/gramps/plugins/webreport/narrativeweb.py
index 5d4ca9e29..d72eba76d 100644
--- a/gramps/plugins/webreport/narrativeweb.py
+++ b/gramps/plugins/webreport/narrativeweb.py
@@ -1948,7 +1948,7 @@ class NavWebOptions(MenuReportOptions):
self.__gallery_changed)
self.__unused = BooleanOption(
- _("Include unused images and media objects"), True)
+ _("Include unused images and media objects"), False)
self.__unused.set_help(_('Whether to include unused or unreferenced'
' media objects'))
addopt("unused", self.__unused)
diff --git a/gramps/plugins/webreport/updates.py b/gramps/plugins/webreport/updates.py
index 8989d49b1..4d752cd16 100644
--- a/gramps/plugins/webreport/updates.py
+++ b/gramps/plugins/webreport/updates.py
@@ -45,9 +45,9 @@ from gramps.plugins.lib.libhtml import Html
#------------------------------------------------
from gramps.plugins.webreport.basepage import BasePage
from gramps.gen.display.place import displayer as _pd
-from gramps.plugins.webreport.common import (FULLCLEAR, _EVENTMAP)
+from gramps.plugins.webreport.common import (FULLCLEAR, _EVENTMAP, html_escape)
from gramps.gen.lib import (Person, Family, Event, Place, Source, Repository,
- Media)
+ Media, Note, Citation)
from gramps.gen.lib.date import Date
_ = glocale.translation.sgettext
@@ -66,6 +66,12 @@ class UpdatesPage(BasePage):
"""
BasePage.__init__(self, report, title)
ldatec = 0
+ self.inc_repository = self.report.options['inc_repository']
+ self.inc_families = self.report.options['inc_families']
+ self.inc_events = self.report.options['inc_events']
+ self.inc_places = self.report.options['inc_places']
+ self.inc_sources = self.report.options['inc_sources']
+ self.inc_gallery = False
output_file, sio = self.report.create_file("updates")
result = self.write_header(self._('New and updated objects'))
@@ -93,35 +99,35 @@ class UpdatesPage(BasePage):
if people is not None:
section += people
- if self.report.options['inc_families']:
+ if self.inc_families:
header = self._("Families")
section += Html("h4", header)
families = self.list_people_changed(Family)
if families is not None:
section += families
- if self.report.options['inc_events']:
+ if self.inc_events:
header = self._("Events")
section += Html("h4", header)
events = self.list_people_changed(Event)
if events is not None:
section += events
- if self.report.options['inc_places']:
+ if self.inc_places:
header = self._("Places")
section += Html("h4", header)
places = self.list_people_changed(Place)
if places is not None:
section += places
- if self.report.options['inc_sources']:
+ if self.inc_sources:
header = self._("Sources")
section += Html("h4", header)
sources = self.list_people_changed(Source)
if sources is not None:
section += sources
- if self.report.options['inc_repository']:
+ if self.inc_repository:
header = self._("Repositories")
section += Html("h4", header)
repos = self.list_people_changed(Repository)
@@ -130,12 +136,19 @@ class UpdatesPage(BasePage):
if (self.report.options['gallery'] and not
self.report.options['create_thumbs_only']):
+ self.inc_gallery = True
header = self._("Media")
section += Html("h4", header)
media = self.list_people_changed(Media)
if media is not None:
section += media
+ header = self._("Notes")
+ section += Html("h4", header)
+ events = self.list_notes()
+ if events is not None:
+ section += events
+
# create clear line for proper styling
# create footer section
footer = self.write_footer(ldatec)
@@ -145,6 +158,122 @@ class UpdatesPage(BasePage):
# and close the file
self.xhtml_writer(homepage, output_file, sio, ldatec)
+ def list_notes(self):
+ """
+ List all notes with last change date
+ """
+ nb_items = 0
+ section = ""
+
+ def sort_on_change(handle):
+ """ sort records based on the last change time """
+ fct = self.report.database.get_note_from_handle
+ obj = fct(handle)
+ timestamp = obj.get_change_time()
+ return timestamp
+
+ note_list = self.report.database.get_note_handles()
+ obj_list = sorted(note_list, key=sort_on_change, reverse=True)
+ with Html("table", class_="list", id="list") as section:
+ for handle in obj_list:
+ show = False
+ date = obj = None
+ obj = self.report.database.get_note_from_handle(handle)
+ if obj:
+ text = html_escape(obj.get()[:50])
+ timestamp = obj.get_change_time()
+ if timestamp - self.maxdays > 0:
+ handle_list = set(
+ self.report.database.find_backlink_handles(
+ handle,
+ include_classes=['Person', 'Family', 'Event',
+ 'Place', 'Media', 'Source',
+ 'Citation', 'Repository',
+ ]))
+ tims = localtime(timestamp)
+ odat = Date(tims.tm_year, tims.tm_mon, tims.tm_mday)
+ date = self.rlocale.date_displayer.display(odat)
+ date += strftime(' %X', tims)
+ if handle_list:
+ srbd = self.report.database
+ srbkref = self.report.bkref_dict
+ for obj_t, r_handle in handle_list:
+ if obj_t == 'Person':
+ if r_handle in srbkref[Person]:
+ name = self.new_person_link(r_handle)
+ show = True
+ elif obj_t == 'Family':
+ if r_handle in srbkref[Family]:
+ fam = srbd.get_family_from_handle(
+ r_handle)
+ fam = self._("Family")
+ name = self.family_link(r_handle, fam)
+ if self.inc_families:
+ show = True
+ elif obj_t == 'Place':
+ if r_handle in srbkref[Place]:
+ plc = srbd.get_place_from_handle(
+ r_handle)
+ plcn = _pd.display(self.report.database,
+ plc)
+ name = self.place_link(r_handle, plcn)
+ if self.inc_places:
+ show = True
+ elif obj_t == 'Event':
+ if r_handle in srbkref[Event]:
+ evt = srbd.get_event_from_handle(
+ r_handle)
+ evtn = self._(evt.get_type().xml_str())
+ name = self.event_link(r_handle, evtn)
+ if self.inc_events:
+ show = True
+ elif obj_t == 'Media':
+ if r_handle in srbkref[Media]:
+ media = srbd.get_media_from_handle(
+ r_handle)
+ evtn = media.get_description()
+ name = self.media_link(r_handle, evtn,
+ evtn,
+ usedescr=False)
+ if self.inc_gallery:
+ show = True
+ elif obj_t == 'Citation':
+ if r_handle in srbkref[Citation]:
+ cit = srbd.get_event_from_handle(
+ r_handle)
+ citsrc = cit.source_handle
+ evtn = self._("Citation")
+ name = self.source_link(citsrc, evtn)
+ if self.inc_sources:
+ show = True
+ elif obj_t == 'Source':
+ if r_handle in srbkref[Source]:
+ src = srbd.get_source_from_handle(
+ r_handle)
+ evtn = src.get_title()
+ name = self.source_link(r_handle, evtn)
+ if self.inc_sources:
+ show = True
+ elif obj_t == 'Repository':
+ if r_handle in srbkref[Repository]:
+ rep = srbd.get_repository_from_handle(
+ r_handle)
+ evtn = rep.get_name()
+ name = self.repository_link(r_handle,
+ evtn)
+ if self.inc_repository:
+ show = True
+ if show:
+ row = Html("tr")
+ section += row
+ row += Html("td", date, class_="date")
+ row += Html("td", text)
+ row += Html("td", name)
+ nb_items += 1
+ if nb_items > self.nbr:
+ break
+ return section
+
def list_people_changed(self, object_type):
"""
List all records with last change date
@@ -180,8 +309,6 @@ class UpdatesPage(BasePage):
key=sort_on_change, reverse=True)
with Html("table", class_="list", id="list") as section:
for handle in obj_list:
- row = Html("tr")
- section += row
date = obj = None
name = ""
obj = fct(handle)
@@ -236,6 +363,8 @@ class UpdatesPage(BasePage):
odat = Date(tims.tm_year, tims.tm_mon, tims.tm_mday)
date = self.rlocale.date_displayer.display(odat)
date += strftime(' %X', tims)
+ row = Html("tr")
+ section += row
row += Html("td", date, class_="date")
row += Html("td", name)
return section
diff --git a/gramps/plugins/webreport/webcal.py b/gramps/plugins/webreport/webcal.py
index f89f4aea6..67322dca1 100644
--- a/gramps/plugins/webreport/webcal.py
+++ b/gramps/plugins/webreport/webcal.py
@@ -63,6 +63,7 @@ from gramps.gen.plug.menu import (BooleanOption, NumberOption, StringOption,
from gramps.gen.utils.config import get_researcher
from gramps.gen.utils.alive import probably_alive
from gramps.gen.utils.db import get_death_or_fallback
+from gramps.gen.utils.symbols import Symbols
from gramps.gen.datehandler import displayer as _dd
from gramps.gen.display.name import displayer as _nd
@@ -464,20 +465,14 @@ class WebCalReport(Report):
"if (x.className === \"nav\") { x.className += \""
" responsive\"; } else { x.className = \"nav\"; }"
" }")
- if self.multiyear:
- head += menuscript
+ head += menuscript
# begin header section
- if self.multiyear:
- headerdiv = Html("div", id='header') + (
- Html("≡")) + (
- Html("h1", html_escape(title),
- id="SiteTitle", inline=True))
- else:
- headerdiv = Html("div", id='header') + (
- Html("h1", html_escape(title),
- id="SiteTitle", inline=True))
+ headerdiv = Html("div", id='header') + (
+ Html("")) + (
+ Html("h1", self.title_text,
+ id="SiteTitle", inline=True))
body += headerdiv
# add body id tag if not None
@@ -2168,12 +2163,14 @@ def get_day_list(event_date, holiday_list, bday_anniv_list, rlocale=glocale):
#age_str.format(precision=1, as_age=False, dlocale=rlocale)
age_str = age_str.format(precision=1, as_age=False, dlocale=rlocale)
+ symbols = Symbols()
+ death_idx = symbols.DEATH_SYMBOL_SHADOWED_LATIN_CROSS
+ death_symbol = symbols.get_death_symbol_for_char(death_idx)
# a birthday
if event == 'Birthday':
if age_at_death is not None:
- death_symbol = "✞" # latin cross for html code
trans_date = trans_text("Died %(death_date)s.")
translated_date = rlocale.get_date(dead_event_date)
mess = trans_date % {'death_date' : translated_date}
@@ -2194,7 +2191,7 @@ def get_day_list(event_date, holiday_list, bday_anniv_list, rlocale=glocale):
# a death
if event == 'Death':
- txt_str = (text + ', '
+ txt_str = (text + ', ' + death_symbol + ' '
+ (_('%s since death') % str(age_str) if nyears
else _('death'))
+ '')