Move "holiday" functions into its own module named libholiday. libholiday is used by all three calendar based reports.
svn: r11759
This commit is contained in:
@@ -10,6 +10,7 @@ SUBDIRS = \
|
|||||||
gramplet \
|
gramplet \
|
||||||
graph \
|
graph \
|
||||||
import \
|
import \
|
||||||
|
lib \
|
||||||
quickview \
|
quickview \
|
||||||
rel \
|
rel \
|
||||||
textreport \
|
textreport \
|
||||||
@@ -20,7 +21,6 @@ pkgdatadir = $(datadir)/@PACKAGE@/plugins
|
|||||||
|
|
||||||
pkgdata_PYTHON = \
|
pkgdata_PYTHON = \
|
||||||
BookReport.py\
|
BookReport.py\
|
||||||
Calendar.py \
|
|
||||||
ExportXml.py\
|
ExportXml.py\
|
||||||
ImportGpkg.py\
|
ImportGpkg.py\
|
||||||
ImportXml.py\
|
ImportXml.py\
|
||||||
|
@@ -2,8 +2,10 @@
|
|||||||
# Gramps - a GTK+/GNOME based genealogy program
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
#
|
#
|
||||||
# Copyright (C) 2007 Thom Sturgill
|
# Copyright (C) 2007 Thom Sturgill
|
||||||
# Copyright (C) 2007-2008 Brian G. Matherly
|
# Copyright (C) 2007-2009 Brian G. Matherly
|
||||||
# Copyright (C) 2008 Rob G. Healey <robhealey1@gmail.com>
|
# Copyright (C) 2008 Rob G. Healey <robhealey1@gmail.com>
|
||||||
|
# Copyright (C) 2008 Jason Simanek
|
||||||
|
# Copyright (C) 2008 Kees Bakker
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Pubilc License as published by
|
# it under the terms of the GNU General Pubilc License as published by
|
||||||
@@ -25,21 +27,6 @@
|
|||||||
"""
|
"""
|
||||||
Web Calendar generator.
|
Web Calendar generator.
|
||||||
|
|
||||||
Menu selection: Reports -> Web Page -> Web Calendar
|
|
||||||
|
|
||||||
Created 4/22/07 by Thom Sturgill based on Calendar.py (with patches)
|
|
||||||
by Doug Blank with input dialog based on NarrativeWeb.py by Don Allingham.
|
|
||||||
|
|
||||||
2008-05-11 Jason Simanek
|
|
||||||
Improving markup for optimal separation of content and presentation.
|
|
||||||
|
|
||||||
2008-June-22 Rob G. Healey
|
|
||||||
*** Remove StyleEditor, make it css based as is NarrativeWeb,
|
|
||||||
move title to first tab, re-word note tabs, complete re-write of
|
|
||||||
calendar build, added year glance, and blank year, added easter and
|
|
||||||
dst start/stop from Calendar.py, etc.
|
|
||||||
|
|
||||||
2008 Kees Bakker
|
|
||||||
Refactoring. This is an ongoing job until this plugin is in a better shape.
|
Refactoring. This is an ongoing job until this plugin is in a better shape.
|
||||||
TODO list:
|
TODO list:
|
||||||
- change filename for one_day pages to yyyy/mm/dd.html (just numbers)
|
- change filename for one_day pages to yyyy/mm/dd.html (just numbers)
|
||||||
@@ -60,12 +47,9 @@ import os
|
|||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
import math
|
|
||||||
import codecs
|
import codecs
|
||||||
import shutil
|
import shutil
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from xml.parsers import expat
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
@@ -90,11 +74,13 @@ from gen.plug.menu import BooleanOption, NumberOption, StringOption, \
|
|||||||
DestinationOption
|
DestinationOption
|
||||||
import Utils
|
import Utils
|
||||||
import GrampsLocale
|
import GrampsLocale
|
||||||
from QuestionDialog import ErrorDialog, WarningDialog
|
from QuestionDialog import WarningDialog
|
||||||
from Utils import probably_alive
|
from Utils import probably_alive
|
||||||
from DateHandler import displayer as _dd
|
from DateHandler import displayer as _dd
|
||||||
from DateHandler import parser as _dp
|
from DateHandler import parser as _dp
|
||||||
|
|
||||||
|
import libholiday
|
||||||
|
|
||||||
#------------------------------------------------------------------------
|
#------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# constants
|
# constants
|
||||||
@@ -256,8 +242,8 @@ class WebCalReport(Report):
|
|||||||
self.author = self.author.replace(',,,', '')
|
self.author = self.author.replace(',,,', '')
|
||||||
self.email = researcher.email
|
self.email = researcher.email
|
||||||
|
|
||||||
self.start_month = 1 # set to January, and it can change
|
self.start_month = 1 # set to January, and it can change
|
||||||
self.end_month = 12 # set to December, this value never changes
|
self.end_month = 12 # set to December, this value never changes
|
||||||
|
|
||||||
today = time.localtime() # set to today's date
|
today = time.localtime() # set to today's date
|
||||||
self.today = datetime.date(today[0], today[1], today[2])
|
self.today = datetime.date(today[0], today[1], today[2])
|
||||||
@@ -316,68 +302,22 @@ class WebCalReport(Report):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
my_date = '...'
|
my_date = '...'
|
||||||
else:
|
else:
|
||||||
my_date = '...' #Incomplete date as in about, circa, etc.
|
my_date = '...' #Incomplete date as in about, circa, etc.
|
||||||
|
|
||||||
day_list.append((text, event, my_date))
|
day_list.append((text, event, my_date))
|
||||||
month_dict[day] = day_list
|
month_dict[day] = day_list
|
||||||
self.calendar[month] = month_dict
|
self.calendar[month] = month_dict
|
||||||
|
|
||||||
def get_holidays(self, year, country="United States"):
|
def __get_holidays(self, year):
|
||||||
"""
|
""" Get the holidays for the specified country and year """
|
||||||
Looks in multiple places for holidays.xml file.
|
holiday_table = libholiday.HolidayTable()
|
||||||
the holidays file will be used first if it exists in user's plugins, otherwise,
|
country = holiday_table.get_countries()[self.country]
|
||||||
the GRAMPS plugins will be checked.
|
holiday_table.load_holidays(year, country)
|
||||||
"""
|
for month in range(1, 13):
|
||||||
|
for day in range(1, 32):
|
||||||
holiday_file = 'holidays.xml'
|
holiday_names = holiday_table.get_holidays(month, day)
|
||||||
holiday_full_path = ""
|
for holiday_name in holiday_names:
|
||||||
fname1 = os.path.join(const.USER_PLUGINS, holiday_file)
|
self.add_holiday_item(holiday_name, year, month, day)
|
||||||
fname2 = os.path.join(const.PLUGINS_DIR, holiday_file)
|
|
||||||
if os.path.exists(fname1):
|
|
||||||
holiday_full_path = fname1
|
|
||||||
elif os.path.exists(fname2):
|
|
||||||
holiday_full_path = fname2
|
|
||||||
if holiday_full_path != "":
|
|
||||||
self.process_holiday_file(year, holiday_full_path, country)
|
|
||||||
|
|
||||||
def process_holiday_file(self, year, filename, country):
|
|
||||||
"""
|
|
||||||
This will process the holidays file for the selected country.
|
|
||||||
|
|
||||||
All holidays, except Easter, and Daylight Saving start/ stop, will be
|
|
||||||
processed from the holidays.xml file. Easter and DST will be handled by
|
|
||||||
specific mathematical formulas within this plugin ...
|
|
||||||
|
|
||||||
"Easter" -- _easter()
|
|
||||||
"Daylight Saving Time" -- _get_dst_start_stop()
|
|
||||||
0 = year, 1 = month, 2 = day
|
|
||||||
"""
|
|
||||||
|
|
||||||
parser = Xml2Obj()
|
|
||||||
element = parser.Parse(filename)
|
|
||||||
holidays_calendar = Holidays(element, country)
|
|
||||||
date = datetime.date(year, 1, 1)
|
|
||||||
while date.year == year:
|
|
||||||
holidays = holidays_calendar.check_date(date)
|
|
||||||
for text in holidays:
|
|
||||||
if text == "Easter": # TODO. Verify if this needs translation, and how
|
|
||||||
easter = _easter(year)
|
|
||||||
self.add_holiday_item(_("Easter"), easter.get_year(), easter.get_month(), easter.get_day())
|
|
||||||
elif text == "Daylight Saving begins":
|
|
||||||
# TODO. There is more than USA and Europe.
|
|
||||||
if Utils.xml_lang() == "en-US": # DST for United States of America
|
|
||||||
dst_start, dst_stop = _get_dst_start_stop(year)
|
|
||||||
else: # DST for Europe
|
|
||||||
dst_start, dst_stop = _get_dst_start_stop(year, "eu")
|
|
||||||
self.add_holiday_item(_("Daylight Saving begins"), \
|
|
||||||
dst_start[0], dst_start[1], dst_start[2])
|
|
||||||
self.add_holiday_item(_("Daylight Saving ends"), \
|
|
||||||
dst_stop[0], dst_stop[1], dst_stop[2])
|
|
||||||
elif text == "Daylight Saving ends":
|
|
||||||
pass # end is already done above
|
|
||||||
else: # not easter, or Daylight Saving Time
|
|
||||||
self.add_holiday_item(text, date.year, date.month, date.day)
|
|
||||||
date = date.fromordinal( date.toordinal() + 1)
|
|
||||||
|
|
||||||
def add_holiday_item(self, text, year, month, day):
|
def add_holiday_item(self, text, year, month, day):
|
||||||
if day == 0:
|
if day == 0:
|
||||||
@@ -481,7 +421,8 @@ class WebCalReport(Report):
|
|||||||
of.write('\t<ul>\n')
|
of.write('\t<ul>\n')
|
||||||
cols = 0
|
cols = 0
|
||||||
cal_year = self.start_year
|
cal_year = self.start_year
|
||||||
while ((0 <= cols < 25) and (self.start_year <= cal_year <= self.end_year)):
|
while ((0 <= cols < 25) and
|
||||||
|
(self.start_year <= cal_year <= self.end_year)):
|
||||||
url = ''
|
url = ''
|
||||||
cs = False
|
cs = False
|
||||||
|
|
||||||
@@ -497,7 +438,8 @@ class WebCalReport(Report):
|
|||||||
if cal_year == self.today.year:
|
if cal_year == self.today.year:
|
||||||
lng_month = _get_long_month_name(self.today.month)
|
lng_month = _get_long_month_name(self.today.month)
|
||||||
|
|
||||||
# Note. We use '/' here because it is a URL, not a OS dependent pathname
|
# Note. We use '/' here because it is a URL, not a OS dependent
|
||||||
|
# pathname.
|
||||||
url = '/'.join(subdirs + [lng_month]) + self.ext
|
url = '/'.join(subdirs + [lng_month]) + self.ext
|
||||||
|
|
||||||
# determine if we need to highlight???
|
# determine if we need to highlight???
|
||||||
@@ -518,7 +460,8 @@ class WebCalReport(Report):
|
|||||||
of.write('\t</ul>\n')
|
of.write('\t</ul>\n')
|
||||||
of.write('</div>\n\n')
|
of.write('</div>\n\n')
|
||||||
|
|
||||||
def calendar_common(self, of, nr_up, year, currsec1, title, body_id, use_home=False, add_print=True):
|
def calendar_common(self, of, nr_up, year, currsec1, title, body_id,
|
||||||
|
use_home=False, add_print=True):
|
||||||
"""
|
"""
|
||||||
Will create the common information for each calendar being created
|
Will create the common information for each calendar being created
|
||||||
"""
|
"""
|
||||||
@@ -591,13 +534,13 @@ class WebCalReport(Report):
|
|||||||
day = col2day[col]
|
day = col2day[col]
|
||||||
return day_names[day]
|
return day_names[day]
|
||||||
|
|
||||||
# monthinfo is filled using standard Python library calendar.monthcalendar
|
# monthinfo is filled using standard Python library
|
||||||
# It fills a list of 7-day-lists. The first day of the 7-day-list is
|
# calendar.monthcalendar. It fills a list of 7-day-lists. The first day
|
||||||
# determined by calendar.firstweekday
|
# of the 7-day-list is determined by calendar.firstweekday.
|
||||||
monthinfo = calendar.monthcalendar(year, month)
|
monthinfo = calendar.monthcalendar(year, month)
|
||||||
|
|
||||||
# Begin calendar head. We'll use the capitalized name, because here it seems
|
# Begin calendar head. We'll use the capitalized name, because here it
|
||||||
# appropriate for most countries.
|
# seems appropriate for most countries.
|
||||||
month_name = lng_month.capitalize()
|
month_name = lng_month.capitalize()
|
||||||
th_txt = month_name
|
th_txt = month_name
|
||||||
if cal == 'wc': # normal_cal()
|
if cal == 'wc': # normal_cal()
|
||||||
@@ -653,11 +596,11 @@ class WebCalReport(Report):
|
|||||||
dayclass = get_class_for_daycol(day_col)
|
dayclass = get_class_for_daycol(day_col)
|
||||||
|
|
||||||
day = week[day_col]
|
day = week[day_col]
|
||||||
if day == 0: # a day in the previous or next month
|
if day == 0: # a day in the previous or next month
|
||||||
if week_row == 0: # a day in the previous month
|
if week_row == 0: # a day in the previous month
|
||||||
specday = lastweek_prevmonth[day_col]
|
specday = lastweek_prevmonth[day_col]
|
||||||
specclass = "previous " + dayclass
|
specclass = "previous " + dayclass
|
||||||
elif week_row == nweeks-1: # a day in the next month
|
elif week_row == nweeks-1: # a day in the next month
|
||||||
specday = firstweek_nextmonth[day_col]
|
specday = firstweek_nextmonth[day_col]
|
||||||
specclass = "next " + dayclass
|
specclass = "next " + dayclass
|
||||||
|
|
||||||
@@ -665,7 +608,7 @@ class WebCalReport(Report):
|
|||||||
of.write('\t\t\t\t<div class="date">%d</div>\n' % specday)
|
of.write('\t\t\t\t<div class="date">%d</div>\n' % specday)
|
||||||
of.write('\t\t\t</td>\n')
|
of.write('\t\t\t</td>\n')
|
||||||
|
|
||||||
else: # normal day number in current month
|
else: # normal day number in current month
|
||||||
thisday = datetime.date.fromordinal(current_ord)
|
thisday = datetime.date.fromordinal(current_ord)
|
||||||
of.write('\t\t\t<td id="%s%02d" ' % (shrt_month, day))
|
of.write('\t\t\t<td id="%s%02d" ' % (shrt_month, day))
|
||||||
if thisday.month == month: # Something this month
|
if thisday.month == month: # Something this month
|
||||||
@@ -799,7 +742,6 @@ class WebCalReport(Report):
|
|||||||
# Note. We use '/' here because it is a URL, not a OS dependent pathname
|
# Note. We use '/' here because it is a URL, not a OS dependent pathname
|
||||||
fname = '/'.join(subdirs + ['images'] + ['somerights20.gif'])
|
fname = '/'.join(subdirs + ['images'] + ['somerights20.gif'])
|
||||||
text = _CC[copy_nr] % {'gif_fname' : fname}
|
text = _CC[copy_nr] % {'gif_fname' : fname}
|
||||||
self.use_copyright = True
|
|
||||||
else:
|
else:
|
||||||
text = "© %s %s" % (self.today.year, self.author)
|
text = "© %s %s" % (self.today.year, self.author)
|
||||||
of.write('\t<p id="copyright">%s</p>\n' % text)
|
of.write('\t<p id="copyright">%s</p>\n' % text)
|
||||||
@@ -981,9 +923,9 @@ class WebCalReport(Report):
|
|||||||
# initialize the holidays dict to fill:
|
# initialize the holidays dict to fill:
|
||||||
self.holidays = {}
|
self.holidays = {}
|
||||||
|
|
||||||
# get the information from holidays for every year being created
|
# get the information, first from holidays:
|
||||||
if self.country != 0: # Don't include holidays
|
if self.country != 0:
|
||||||
self.get_holidays(cal_year, _COUNTRIES[self.country]) # _country is currently global
|
self.__get_holidays(cal_year)
|
||||||
|
|
||||||
# adjust the months being created if self.partyear is True,
|
# adjust the months being created if self.partyear is True,
|
||||||
# and if the year is the current year, then start month is current month
|
# and if the year is the current year, then start month is current month
|
||||||
@@ -1004,10 +946,10 @@ class WebCalReport(Report):
|
|||||||
cal_year = self.start_year
|
cal_year = self.start_year
|
||||||
|
|
||||||
self.holidays = {}
|
self.holidays = {}
|
||||||
|
|
||||||
# get the information from holidays for each year being created
|
# get the information, first from holidays:
|
||||||
if self.country != 0: # Don't include holidays
|
if self.country != 0:
|
||||||
self.get_holidays(cal_year, _COUNTRIES[self.country]) # _COUNTRIES is currently global
|
self.__get_holidays(cal_year)
|
||||||
|
|
||||||
# generate progress pass for single year
|
# generate progress pass for single year
|
||||||
#self.progress.set_pass(_('Creating calendars'), self.end_month - self.start_month)
|
#self.progress.set_pass(_('Creating calendars'), self.end_month - self.start_month)
|
||||||
@@ -1263,7 +1205,7 @@ class WebCalOptions(MenuReportOptions):
|
|||||||
# set to today's date for use in menu, etc.
|
# set to today's date for use in menu, etc.
|
||||||
# 0 = year, 1 = month, 2 = day
|
# 0 = year, 1 = month, 2 = day
|
||||||
today = time.localtime()
|
today = time.localtime()
|
||||||
self.today = datetime.date(today[0], today[1], today[2])
|
today = datetime.date(today[0], today[1], today[2])
|
||||||
|
|
||||||
partyear = BooleanOption(_('Create Partial Year calendar'), False)
|
partyear = BooleanOption(_('Create Partial Year calendar'), False)
|
||||||
partyear.set_help(_('Create a partial year calendar. The start month will be'
|
partyear.set_help(_('Create a partial year calendar. The start month will be'
|
||||||
@@ -1275,11 +1217,11 @@ class WebCalOptions(MenuReportOptions):
|
|||||||
menu.add_option(category_name, 'multiyear', self.__multiyear)
|
menu.add_option(category_name, 'multiyear', self.__multiyear)
|
||||||
self.__multiyear.connect('value-changed', self.__multiyear_changed)
|
self.__multiyear.connect('value-changed', self.__multiyear_changed)
|
||||||
|
|
||||||
self.__start_year = NumberOption(_('Start Year for the Calendar(s)'), self.today.year, 1900, 3000)
|
self.__start_year = NumberOption(_('Start Year for the Calendar(s)'), today.year, 1900, 3000)
|
||||||
self.__start_year.set_help(_('Enter the starting year for the calendars between 1900 - 3000'))
|
self.__start_year.set_help(_('Enter the starting year for the calendars between 1900 - 3000'))
|
||||||
menu.add_option(category_name, 'start_year', self.__start_year)
|
menu.add_option(category_name, 'start_year', self.__start_year)
|
||||||
|
|
||||||
self.__end_year = NumberOption(_('End Year for the Calendar(s)'), self.today.year, 1900, 3000)
|
self.__end_year = NumberOption(_('End Year for the Calendar(s)'), today.year, 1900, 3000)
|
||||||
self.__end_year.set_help(_('Enter the ending year for the calendars between 1900 - 3000.'
|
self.__end_year.set_help(_('Enter the ending year for the calendars between 1900 - 3000.'
|
||||||
' if multiple years is selected, then only twenty years at any given time'))
|
' if multiple years is selected, then only twenty years at any given time'))
|
||||||
menu.add_option(category_name, 'end_year', self.__end_year)
|
menu.add_option(category_name, 'end_year', self.__end_year)
|
||||||
@@ -1291,7 +1233,8 @@ class WebCalOptions(MenuReportOptions):
|
|||||||
menu.add_option(category_name, 'fullyear', fullyear)
|
menu.add_option(category_name, 'fullyear', fullyear)
|
||||||
|
|
||||||
country = EnumeratedListOption(_('Country for holidays'), 0 )
|
country = EnumeratedListOption(_('Country for holidays'), 0 )
|
||||||
for index, item in enumerate(_COUNTRIES):
|
holiday_table = libholiday.HolidayTable()
|
||||||
|
for index, item in enumerate(holiday_table.get_countries()):
|
||||||
country.add_item(index, item)
|
country.add_item(index, item)
|
||||||
country.set_help(_("Holidays will be included for the selected "
|
country.set_help(_("Holidays will be included for the selected "
|
||||||
"country"))
|
"country"))
|
||||||
@@ -1417,292 +1360,6 @@ class WebCalOptions(MenuReportOptions):
|
|||||||
else:
|
else:
|
||||||
self.__end_year.set_available(False)
|
self.__end_year.set_available(False)
|
||||||
|
|
||||||
#------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#------------------------------------------------------------------------
|
|
||||||
class Element:
|
|
||||||
""" A parsed XML element """
|
|
||||||
def __init__(self, name, attributes):
|
|
||||||
'Element constructor'
|
|
||||||
# The element's tag name
|
|
||||||
self.name = name
|
|
||||||
# The element's attribute dictionary
|
|
||||||
self.attributes = attributes
|
|
||||||
# The element's cdata
|
|
||||||
self.cdata = ''
|
|
||||||
# The element's child element list (sequence)
|
|
||||||
self.children = []
|
|
||||||
|
|
||||||
def addChild(self, element):
|
|
||||||
'Add a reference to a child element'
|
|
||||||
self.children.append(element)
|
|
||||||
|
|
||||||
def getAttribute(self, key):
|
|
||||||
'Get an attribute value'
|
|
||||||
return self.attributes.get(key)
|
|
||||||
|
|
||||||
def getData(self):
|
|
||||||
'Get the cdata'
|
|
||||||
return self.cdata
|
|
||||||
|
|
||||||
def getElements(self, name=''):
|
|
||||||
'Get a list of child elements'
|
|
||||||
#If no tag name is specified, return the all children
|
|
||||||
if not name:
|
|
||||||
return self.children
|
|
||||||
else:
|
|
||||||
# else return only those children with a matching tag name
|
|
||||||
elements = []
|
|
||||||
for element in self.children:
|
|
||||||
if element.name == name:
|
|
||||||
elements.append(element)
|
|
||||||
return elements
|
|
||||||
|
|
||||||
def toString(self, level=0):
|
|
||||||
retval = " " * level
|
|
||||||
retval += "<%s" % self.name
|
|
||||||
for attribute in self.attributes:
|
|
||||||
retval += " %s=\"%s\"" % (attribute, self.attributes[attribute])
|
|
||||||
c = ""
|
|
||||||
for child in self.children:
|
|
||||||
c += child.toString(level+1)
|
|
||||||
if c == "":
|
|
||||||
retval += "/>\n"
|
|
||||||
else:
|
|
||||||
retval += ">\n" + c + ("</%s>\n" % self.name)
|
|
||||||
return retval
|
|
||||||
|
|
||||||
|
|
||||||
class Xml2Obj:
|
|
||||||
""" XML to Object """
|
|
||||||
def __init__(self):
|
|
||||||
self.root = None
|
|
||||||
self.nodeStack = []
|
|
||||||
|
|
||||||
def StartElement(self, name, attributes):
|
|
||||||
'SAX start element even handler'
|
|
||||||
# Instantiate an Element object
|
|
||||||
element = Element(name.encode(), attributes)
|
|
||||||
# Push element onto the stack and make it a child of parent
|
|
||||||
if len(self.nodeStack) > 0:
|
|
||||||
parent = self.nodeStack[-1]
|
|
||||||
parent.addChild(element)
|
|
||||||
else:
|
|
||||||
self.root = element
|
|
||||||
self.nodeStack.append(element)
|
|
||||||
|
|
||||||
def EndElement(self, name):
|
|
||||||
'SAX end element event handler'
|
|
||||||
self.nodeStack = self.nodeStack[:-1]
|
|
||||||
|
|
||||||
def CharacterData(self, data):
|
|
||||||
'SAX character data event handler'
|
|
||||||
if data.strip():
|
|
||||||
data = data.encode()
|
|
||||||
element = self.nodeStack[-1]
|
|
||||||
element.cdata += data
|
|
||||||
return
|
|
||||||
|
|
||||||
def Parse(self, filename):
|
|
||||||
# Create a SAX parser
|
|
||||||
Parser = expat.ParserCreate()
|
|
||||||
# SAX event handlers
|
|
||||||
Parser.StartElementHandler = self.StartElement
|
|
||||||
Parser.EndElementHandler = self.EndElement
|
|
||||||
Parser.CharacterDataHandler = self.CharacterData
|
|
||||||
# Parse the XML File
|
|
||||||
ParserStatus = Parser.Parse(open(filename, 'r').read(), 1)
|
|
||||||
return self.root
|
|
||||||
|
|
||||||
class Holidays:
|
|
||||||
""" Class used to read XML holidays to add to calendar. """
|
|
||||||
def __init__(self, elements, country="US"):
|
|
||||||
self.debug = 0
|
|
||||||
self.elements = elements
|
|
||||||
self.country = country
|
|
||||||
self.dates = []
|
|
||||||
self.initialize()
|
|
||||||
|
|
||||||
def set_country(self, country):
|
|
||||||
self.country = country
|
|
||||||
self.dates = []
|
|
||||||
self.initialize()
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
# parse the date objects
|
|
||||||
for country_set in self.elements.children:
|
|
||||||
if country_set.name == "country" and country_set.attributes["name"] == self.country:
|
|
||||||
for date in country_set.children:
|
|
||||||
if date.name == "date":
|
|
||||||
data = {"value" : "",
|
|
||||||
"name" : "",
|
|
||||||
"offset": "",
|
|
||||||
"type": "",
|
|
||||||
"if": "",
|
|
||||||
} # defaults
|
|
||||||
for attr in date.attributes:
|
|
||||||
data[attr] = date.attributes[attr]
|
|
||||||
self.dates.append(data)
|
|
||||||
|
|
||||||
def get_daynames(self, y, m, dayname):
|
|
||||||
if self.debug:
|
|
||||||
print "%s's in %d %d..." % (dayname, m, y)
|
|
||||||
retval = [0]
|
|
||||||
dow = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].index(dayname)
|
|
||||||
for d in range(1, 32):
|
|
||||||
try:
|
|
||||||
date = datetime.date(y, m, d)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
if date.weekday() == dow:
|
|
||||||
retval.append( d )
|
|
||||||
if self.debug:
|
|
||||||
print "dow=", dow, "days=", retval
|
|
||||||
return retval
|
|
||||||
|
|
||||||
def check_date(self, date):
|
|
||||||
retval = []
|
|
||||||
for rule in self.dates:
|
|
||||||
if self.debug:
|
|
||||||
print "Checking ", rule["name"], "..."
|
|
||||||
offset = 0
|
|
||||||
if rule["offset"] != "":
|
|
||||||
if rule["offset"].isdigit():
|
|
||||||
offset = int(rule["offset"])
|
|
||||||
elif rule["offset"][0] in ["-", "+"] and rule["offset"][1:].isdigit():
|
|
||||||
offset = int(rule["offset"])
|
|
||||||
else:
|
|
||||||
# must be a dayname
|
|
||||||
offset = rule["offset"]
|
|
||||||
if rule["value"].count("/") == 3: # year/num/day/month, "3rd wednesday in april"
|
|
||||||
y, num, dayname, mon = rule["value"].split("/")
|
|
||||||
if y == "*":
|
|
||||||
y = date.year
|
|
||||||
else:
|
|
||||||
y = int(y)
|
|
||||||
if mon.isdigit():
|
|
||||||
m = int(mon)
|
|
||||||
elif mon == "*":
|
|
||||||
m = date.month
|
|
||||||
else:
|
|
||||||
m = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
|
|
||||||
'jul', 'aug', 'sep', 'oct', 'nov', 'dec'].index(mon) + 1
|
|
||||||
dates_of_dayname = self.get_daynames(y, m, dayname)
|
|
||||||
if self.debug:
|
|
||||||
print "num =", num
|
|
||||||
d = dates_of_dayname[int(num)]
|
|
||||||
elif rule["value"].count("/") == 2: # year/month/day
|
|
||||||
y, m, d = rule["value"].split("/")
|
|
||||||
if y == "*":
|
|
||||||
y = date.year
|
|
||||||
else:
|
|
||||||
y = int(y)
|
|
||||||
if m == "*":
|
|
||||||
m = date.month
|
|
||||||
else:
|
|
||||||
m = int(m)
|
|
||||||
if d == "*":
|
|
||||||
d = date.day
|
|
||||||
else:
|
|
||||||
d = int(d)
|
|
||||||
ndate = datetime.date(y, m, d)
|
|
||||||
if self.debug:
|
|
||||||
print ndate, offset, type(offset)
|
|
||||||
if isinstance(offset, int):
|
|
||||||
if offset != 0:
|
|
||||||
ndate = ndate.fromordinal(ndate.toordinal() + offset)
|
|
||||||
elif isinstance(offset, basestring):
|
|
||||||
dir_ = 1
|
|
||||||
if offset[0] == "-":
|
|
||||||
dir_ = -1
|
|
||||||
offset = offset[1:]
|
|
||||||
if offset in ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']:
|
|
||||||
# next tuesday you come to, including this one
|
|
||||||
dow = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].index(offset)
|
|
||||||
ord_ = ndate.toordinal()
|
|
||||||
while ndate.fromordinal(ord_).weekday() != dow:
|
|
||||||
ord_ += dir_
|
|
||||||
ndate = ndate.fromordinal(ord_)
|
|
||||||
if self.debug:
|
|
||||||
print "ndate:", ndate, "date:", date
|
|
||||||
if ndate == date:
|
|
||||||
if rule["if"] != "":
|
|
||||||
if not eval(rule["if"]):
|
|
||||||
continue
|
|
||||||
retval.append(rule["name"])
|
|
||||||
return retval
|
|
||||||
|
|
||||||
def _get_countries_from_holiday_file(filename):
|
|
||||||
""" This will process a holiday file for country names """
|
|
||||||
parser = Xml2Obj()
|
|
||||||
element = parser.Parse(filename)
|
|
||||||
country_list = []
|
|
||||||
for country_set in element.children:
|
|
||||||
if country_set.name == "country":
|
|
||||||
if country_set.attributes["name"] not in country_list:
|
|
||||||
country_list.append(country_set.attributes["name"])
|
|
||||||
return country_list
|
|
||||||
|
|
||||||
def _get_countries():
|
|
||||||
""" Looks in multiple places for holidays.xml files """
|
|
||||||
locations = [const.PLUGINS_DIR, const.USER_PLUGINS]
|
|
||||||
holiday_file = 'holidays.xml'
|
|
||||||
country_list = []
|
|
||||||
for dir_ in locations:
|
|
||||||
holiday_full_path = os.path.join(dir_, holiday_file)
|
|
||||||
if os.path.exists(holiday_full_path):
|
|
||||||
cs = _get_countries_from_holiday_file(holiday_full_path)
|
|
||||||
for c in cs:
|
|
||||||
if c not in country_list:
|
|
||||||
country_list.append(c)
|
|
||||||
country_list.sort()
|
|
||||||
country_list.insert(0, _("Don't include holidays"))
|
|
||||||
return country_list
|
|
||||||
|
|
||||||
# TODO: Only load this once the first time it is actually needed so Gramps
|
|
||||||
# doesn't take so long to start up.
|
|
||||||
_COUNTRIES = _get_countries()
|
|
||||||
|
|
||||||
# code snippets for Easter and Daylight saving start/ stop
|
|
||||||
# are borrowed from Calendar.py
|
|
||||||
def _easter(year):
|
|
||||||
"""
|
|
||||||
Computes the year/month/day of easter. Based on work by
|
|
||||||
J.-M. Oudin (1940) and is reprinted in the "Explanatory Supplement
|
|
||||||
to the Astronomical Almanac", ed. P. K. Seidelmann (1992). Note:
|
|
||||||
Ash Wednesday is 46 days before Easter Sunday.
|
|
||||||
"""
|
|
||||||
c = year / 100
|
|
||||||
n = year - 19 * (year / 19)
|
|
||||||
k = (c - 17) / 25
|
|
||||||
i = c - c / 4 - (c - k) / 3 + 19 * n + 15
|
|
||||||
i = i - 30 * (i / 30)
|
|
||||||
i = i - (i / 28) * (1 - (i / 28) * (29 / (i + 1)) * ((21 - n) / 11))
|
|
||||||
j = year + year / 4 + i + 2 - c + c / 4
|
|
||||||
j = j - 7 * (j / 7)
|
|
||||||
l = i - j
|
|
||||||
month = 3 + (l + 40) / 44
|
|
||||||
day = l + 28 - 31 * (month / 4)
|
|
||||||
return _make_date(year, month, day)
|
|
||||||
|
|
||||||
def _get_dst_start_stop(year, area="us"):
|
|
||||||
"""
|
|
||||||
Return Daylight Saving Time start/stop in a given area ("us", "eu").
|
|
||||||
US calculation valid 1976-2099; EU 1996-2099
|
|
||||||
"""
|
|
||||||
if area == "us":
|
|
||||||
if year > 2006:
|
|
||||||
start = "%d/%d/%d" % (year, 3, 14 - (math.floor(1 + year * 5 / 4) % 7)) # March
|
|
||||||
stop = "%d/%d/%d" % (year, 11, 7 - (math.floor(1 + year * 5 / 4) % 7)) # November
|
|
||||||
else:
|
|
||||||
start = "%d/%d/%d" % (year, 4, (2 + 6 * year - math.floor(year / 4)) % 7 + 1) # April
|
|
||||||
stop = "%d/%d/%d" % (year, 10, (31 - (math.floor(year * 5 / 4) + 1) % 7)) # October
|
|
||||||
elif area == "eu":
|
|
||||||
start = "%d/%d/%d" % (year, 3, (31 - (math.floor(year * 5 / 4) + 4) % 7)) # March
|
|
||||||
stop = "%d/%d/%d" % (year, 10, (31 - (math.floor(year * 5 / 4) + 1) % 7)) # Oct
|
|
||||||
return (start, stop)
|
|
||||||
|
|
||||||
def _get_regular_surname(sex, name):
|
def _get_regular_surname(sex, name):
|
||||||
"""
|
"""
|
||||||
|
File diff suppressed because it is too large
Load Diff
21
src/plugins/lib/Makefile.am
Normal file
21
src/plugins/lib/Makefile.am
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# This is the src/plugins/lib level Makefile for Gramps
|
||||||
|
# We could use GNU make's ':=' syntax for nice wildcard use,
|
||||||
|
# but that is not necessarily portable.
|
||||||
|
# If not using GNU make, then list all .py files individually
|
||||||
|
|
||||||
|
pkgdatadir = $(datadir)/@PACKAGE@/plugins/lib
|
||||||
|
|
||||||
|
pkgdata_PYTHON = \
|
||||||
|
libholiday.py
|
||||||
|
|
||||||
|
pkgpyexecdir = @pkgpyexecdir@/plugins/lib
|
||||||
|
pkgpythondir = @pkgpythondir@/plugins/lib
|
||||||
|
|
||||||
|
# Clean up all the byte-compiled files
|
||||||
|
MOSTLYCLEANFILES = *pyc *pyo
|
||||||
|
|
||||||
|
GRAMPS_PY_MODPATH = "../../"
|
||||||
|
|
||||||
|
pycheck:
|
||||||
|
(export PYTHONPATH=$(GRAMPS_PY_MODPATH); \
|
||||||
|
pychecker $(pkgdata_PYTHON));
|
441
src/plugins/lib/libholiday.py
Normal file
441
src/plugins/lib/libholiday.py
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||||
|
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# python modules
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
from gettext import gettext as _
|
||||||
|
from xml.parsers import expat
|
||||||
|
import datetime
|
||||||
|
import math
|
||||||
|
import const
|
||||||
|
import os
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# GRAMPS modules
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
from gen.plug import PluginManager, Plugin
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Support functions
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
def easter(year):
|
||||||
|
"""
|
||||||
|
Computes the year/month/day of easter. Based on work by
|
||||||
|
J.-M. Oudin (1940) and is reprinted in the "Explanatory Supplement
|
||||||
|
to the Astronomical Almanac", ed. P. K. Seidelmann (1992). Note:
|
||||||
|
Ash Wednesday is 46 days before Easter Sunday.
|
||||||
|
"""
|
||||||
|
c = year / 100
|
||||||
|
n = year - 19 * (year / 19)
|
||||||
|
k = (c - 17) / 25
|
||||||
|
i = c - c / 4 - (c - k) / 3 + 19 * n + 15
|
||||||
|
i = i - 30 * (i / 30)
|
||||||
|
i = i - (i / 28) * (1 - (i / 28) * (29 / (i + 1))
|
||||||
|
* ((21 - n) / 11))
|
||||||
|
j = year + year / 4 + i + 2 - c + c / 4
|
||||||
|
j = j - 7 * (j / 7)
|
||||||
|
l = i - j
|
||||||
|
month = 3 + (l + 40) / 44
|
||||||
|
day = l + 28 - 31 * (month / 4)
|
||||||
|
return "%d/%d/%d" % (year, month, day)
|
||||||
|
|
||||||
|
def g2iso(dow):
|
||||||
|
""" Converst GRAMPS day of week to ISO day of week """
|
||||||
|
# GRAMPS: SUN = 1
|
||||||
|
# ISO: MON = 1
|
||||||
|
return (dow + 5) % 7 + 1
|
||||||
|
|
||||||
|
def dst(year, area="us"):
|
||||||
|
"""
|
||||||
|
Return Daylight Saving Time start/stop in a given area ("us", "eu").
|
||||||
|
US calculation valid 1976-2099; EU 1996-2099
|
||||||
|
"""
|
||||||
|
if area == "us":
|
||||||
|
if year > 2006:
|
||||||
|
start = "%d/%d/%d" % (year, 3, 14 - (math.floor(1 + year * 5 / 4) % 7)) # March
|
||||||
|
stop = "%d/%d/%d" % (year, 11, 7 - (math.floor(1 + year * 5 / 4) % 7)) # November
|
||||||
|
else:
|
||||||
|
start = "%d/%d/%d" % (year, 4, (2 + 6 * year - math.floor(year / 4)) % 7 + 1) # April
|
||||||
|
stop = "%d/%d/%d" % (year, 10, (31 - (math.floor(year * 5 / 4) + 1) % 7)) # October
|
||||||
|
elif area == "eu":
|
||||||
|
start = "%d/%d/%d" % (year, 3, (31 - (math.floor(year * 5 / 4) + 4) % 7)) # March
|
||||||
|
stop = "%d/%d/%d" % (year, 10, (31 - (math.floor(year * 5 / 4) + 1) % 7)) # Oct
|
||||||
|
return (start, stop)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# HolidayTable
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class HolidayTable:
|
||||||
|
"""
|
||||||
|
HolidayTable is a class which provides holidays for various
|
||||||
|
countries and years.
|
||||||
|
"""
|
||||||
|
__holiday_files = []
|
||||||
|
__countries = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Find the holiday files and load the countries if it has not already
|
||||||
|
been done.
|
||||||
|
"""
|
||||||
|
if( not HolidayTable.__holiday_files ):
|
||||||
|
self.__find_holiday_files()
|
||||||
|
|
||||||
|
if( not HolidayTable.__countries ):
|
||||||
|
self.__build_country_list()
|
||||||
|
|
||||||
|
# Initialize the holiday table to be empty
|
||||||
|
self.__holidays = {}
|
||||||
|
self.__init_table()
|
||||||
|
|
||||||
|
def __find_holiday_files(self):
|
||||||
|
""" Looks in multiple places for holidays.xml files """
|
||||||
|
holiday_file = 'holidays.xml'
|
||||||
|
|
||||||
|
# Look for holiday files in the installation plugins directory and all
|
||||||
|
# subdirectories.
|
||||||
|
holiday_full_path = os.path.join(const.PLUGINS_DIR, holiday_file)
|
||||||
|
if os.path.exists(holiday_full_path):
|
||||||
|
HolidayTable.__holiday_files.append(holiday_full_path)
|
||||||
|
|
||||||
|
for (dirpath, dirnames, filenames) in os.walk(const.PLUGINS_DIR):
|
||||||
|
for directory in dirnames:
|
||||||
|
holiday_full_path = os.path.join(directory, holiday_file)
|
||||||
|
if os.path.exists(holiday_full_path):
|
||||||
|
HolidayTable.__holiday_files.append(holiday_full_path)
|
||||||
|
|
||||||
|
# Look for holiday files in the user plugins directory and all
|
||||||
|
# subdirectories.
|
||||||
|
holiday_full_path = os.path.join(const.USER_PLUGINS, holiday_file)
|
||||||
|
if os.path.exists(holiday_full_path):
|
||||||
|
HolidayTable.__holiday_files.append(holiday_full_path)
|
||||||
|
|
||||||
|
for (dirpath, dirnames, filenames) in os.walk(const.USER_PLUGINS):
|
||||||
|
for directory in dirnames:
|
||||||
|
holiday_full_path = os.path.join(directory, holiday_file)
|
||||||
|
if os.path.exists(holiday_full_path):
|
||||||
|
HolidayTable.__holiday_files.append(holiday_full_path)
|
||||||
|
|
||||||
|
def __build_country_list(self):
|
||||||
|
""" Generate the list of countries that have holiday information. """
|
||||||
|
for holiday_file_path in HolidayTable.__holiday_files:
|
||||||
|
parser = _Xml2Obj()
|
||||||
|
root_element = parser.parse(holiday_file_path)
|
||||||
|
for country_element in root_element.get_children():
|
||||||
|
if country_element.get_name() == "country":
|
||||||
|
country_name = country_element.get_attribute("name")
|
||||||
|
if country_name not in HolidayTable.__countries:
|
||||||
|
HolidayTable.__countries.append(country_name)
|
||||||
|
|
||||||
|
def __init_table(self):
|
||||||
|
""" Initialize the holiday table structure. """
|
||||||
|
for month in range(1, 13):
|
||||||
|
self.__holidays[month] = {}
|
||||||
|
for day in range(1, 32):
|
||||||
|
self.__holidays[month][day] = []
|
||||||
|
|
||||||
|
def get_countries(self):
|
||||||
|
"""
|
||||||
|
Get all the country names that holidays are available for.
|
||||||
|
|
||||||
|
@return: nothing
|
||||||
|
"""
|
||||||
|
return HolidayTable.__countries
|
||||||
|
|
||||||
|
def load_holidays(self, year, country):
|
||||||
|
"""
|
||||||
|
Load the holiday table for the specified year and country.
|
||||||
|
This must be called before get_holidays().
|
||||||
|
|
||||||
|
@param year: The year for which the holidays should be loaded.
|
||||||
|
Example: 2010
|
||||||
|
@type year: int
|
||||||
|
@param country: The country for which the holidays should be loaded.
|
||||||
|
Example: "United States"
|
||||||
|
@type country: str
|
||||||
|
@return: nothing
|
||||||
|
"""
|
||||||
|
self.__init_table()
|
||||||
|
for holiday_file_path in HolidayTable.__holiday_files:
|
||||||
|
parser = _Xml2Obj()
|
||||||
|
element = parser.parse(holiday_file_path)
|
||||||
|
calendar = _Holidays(element, country)
|
||||||
|
date = datetime.date(year, 1, 1)
|
||||||
|
while date.year == year:
|
||||||
|
holidays = calendar.check_date(date)
|
||||||
|
for text in holidays:
|
||||||
|
self.__holidays[date.month][date.day].append(text)
|
||||||
|
date = date.fromordinal(date.toordinal() + 1)
|
||||||
|
|
||||||
|
def get_holidays(self, month, day):
|
||||||
|
"""
|
||||||
|
Get the holidays for the given day of the year.
|
||||||
|
|
||||||
|
@param month: The month for the requested holidays.
|
||||||
|
Example: 1
|
||||||
|
@type month: int
|
||||||
|
@param month: The day for the requested holidays.
|
||||||
|
Example: 1
|
||||||
|
@type month: int
|
||||||
|
@return: An array of strings with holiday names.
|
||||||
|
@return type: [str]
|
||||||
|
"""
|
||||||
|
return self.__holidays[month][day]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# _Element
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class _Element:
|
||||||
|
""" A parsed XML element """
|
||||||
|
def __init__(self, name, attributes):
|
||||||
|
'Element constructor'
|
||||||
|
# The element's tag name
|
||||||
|
self.__name = name
|
||||||
|
# The element's attribute dictionary
|
||||||
|
self.__attributes = attributes
|
||||||
|
# The element's child element list (sequence)
|
||||||
|
self.__children = []
|
||||||
|
|
||||||
|
def add_child(self, element):
|
||||||
|
'Add a reference to a child element'
|
||||||
|
self.__children.append(element)
|
||||||
|
|
||||||
|
def get_attribute(self, key):
|
||||||
|
'Get an attribute value'
|
||||||
|
return self.__attributes.get(key)
|
||||||
|
|
||||||
|
def get_attributes(self):
|
||||||
|
'Get all the attributes'
|
||||||
|
return self.__attributes
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
""" Get the name of this element. """
|
||||||
|
return self.__name
|
||||||
|
|
||||||
|
def get_children(self):
|
||||||
|
""" Get the children elements for this element. """
|
||||||
|
return self.__children
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# _Xml2Obj
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class _Xml2Obj:
|
||||||
|
""" XML to Object """
|
||||||
|
def __init__(self):
|
||||||
|
self.root = None
|
||||||
|
self.nodeStack = []
|
||||||
|
|
||||||
|
def start_element(self, name, attributes):
|
||||||
|
'SAX start element even handler'
|
||||||
|
# Instantiate an Element object
|
||||||
|
element = _Element(name.encode(), attributes)
|
||||||
|
# Push element onto the stack and make it a child of parent
|
||||||
|
if len(self.nodeStack) > 0:
|
||||||
|
parent = self.nodeStack[-1]
|
||||||
|
parent.add_child(element)
|
||||||
|
else:
|
||||||
|
self.root = element
|
||||||
|
self.nodeStack.append(element)
|
||||||
|
|
||||||
|
def end_element(self, name):
|
||||||
|
'SAX end element event handler'
|
||||||
|
self.nodeStack = self.nodeStack[:-1]
|
||||||
|
|
||||||
|
def parse(self, filename):
|
||||||
|
'Create a SAX parser and parse filename '
|
||||||
|
parser = expat.ParserCreate()
|
||||||
|
# SAX event handlers
|
||||||
|
parser.StartElementHandler = self.start_element
|
||||||
|
parser.EndElementHandler = self.end_element
|
||||||
|
# Parse the XML File
|
||||||
|
parser.Parse(open(filename, 'r').read(), 1)
|
||||||
|
return self.root
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# _Holidays
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class _Holidays:
|
||||||
|
""" Class used to read XML holidays to add to calendar. """
|
||||||
|
def __init__(self, elements, country="US"):
|
||||||
|
self.debug = 0
|
||||||
|
self.elements = elements
|
||||||
|
self.country = country
|
||||||
|
self.dates = []
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
|
def set_country(self, country):
|
||||||
|
""" Set the contry of holidays to read """
|
||||||
|
self.country = country
|
||||||
|
self.dates = []
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
""" Parse the holiday date XML items """
|
||||||
|
for country_set in self.elements.get_children():
|
||||||
|
if country_set.get_name() == "country" and \
|
||||||
|
country_set.get_attribute("name") == self.country:
|
||||||
|
for date in country_set.get_children():
|
||||||
|
if date.get_name() == "date":
|
||||||
|
data = {"value" : "",
|
||||||
|
"name" : "",
|
||||||
|
"offset": "",
|
||||||
|
"type": "",
|
||||||
|
"if": "",
|
||||||
|
} # defaults
|
||||||
|
for attr in date.get_attributes():
|
||||||
|
data[attr] = date.get_attribute(attr)
|
||||||
|
self.dates.append(data)
|
||||||
|
|
||||||
|
def get_daynames(self, year, month, dayname):
|
||||||
|
""" Get the items for a particular year/month and day of week """
|
||||||
|
if self.debug:
|
||||||
|
print "%s's in %d %d..." % (dayname, month, year)
|
||||||
|
|
||||||
|
retval = [0]
|
||||||
|
dow = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].index(dayname)
|
||||||
|
for day in range(1, 32):
|
||||||
|
try:
|
||||||
|
date = datetime.date(year, month, day)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if date.weekday() == dow:
|
||||||
|
retval.append(day)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print "dow=", dow, "days=", retval
|
||||||
|
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def check_date(self, date):
|
||||||
|
""" Return items that match rules """
|
||||||
|
retval = []
|
||||||
|
for rule in self.dates:
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print "Checking ", rule["name"], "..."
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
if rule["offset"] != "":
|
||||||
|
if rule["offset"].isdigit():
|
||||||
|
offset = int(rule["offset"])
|
||||||
|
elif rule["offset"][0] in ["-", "+"] and \
|
||||||
|
rule["offset"][1:].isdigit():
|
||||||
|
offset = int(rule["offset"])
|
||||||
|
else:
|
||||||
|
# must be a dayname
|
||||||
|
offset = rule["offset"]
|
||||||
|
if len(rule["value"]) > 0 and rule["value"][0] == '>':
|
||||||
|
# eval exp -> year/num[/day[/month]]
|
||||||
|
y, m, d = date.year, date.month, date.day
|
||||||
|
rule["value"] = eval(rule["value"][1:])
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print "rule['value']:", rule["value"]
|
||||||
|
|
||||||
|
if rule["value"].count("/") == 3: # year/num/day/month, "3rd wednesday in april"
|
||||||
|
y, num, dayname, mon = rule["value"].split("/")
|
||||||
|
if y == "*":
|
||||||
|
y = date.year
|
||||||
|
else:
|
||||||
|
y = int(y)
|
||||||
|
if mon.isdigit():
|
||||||
|
m = int(mon)
|
||||||
|
elif mon == "*":
|
||||||
|
m = date.month
|
||||||
|
else:
|
||||||
|
m = ['jan', 'feb', 'mar', 'apr', 'may', 'jun',
|
||||||
|
'jul', 'aug', 'sep', 'oct', 'nov', 'dec'].index(mon) + 1
|
||||||
|
dates_of_dayname = self.get_daynames(y, m, dayname)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print "num =", num
|
||||||
|
|
||||||
|
d = dates_of_dayname[int(num)]
|
||||||
|
elif rule["value"].count("/") == 2: # year/month/day
|
||||||
|
y, m, d = rule["value"].split("/")
|
||||||
|
if y == "*":
|
||||||
|
y = date.year
|
||||||
|
else:
|
||||||
|
y = int(y)
|
||||||
|
if m == "*":
|
||||||
|
m = date.month
|
||||||
|
else:
|
||||||
|
m = int(m)
|
||||||
|
if d == "*":
|
||||||
|
d = date.day
|
||||||
|
else:
|
||||||
|
d = int(d)
|
||||||
|
ndate = datetime.date(y, m, d)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print ndate, offset, type(offset)
|
||||||
|
|
||||||
|
if isinstance(offset, int):
|
||||||
|
if offset != 0:
|
||||||
|
ndate = ndate.fromordinal(ndate.toordinal() + offset)
|
||||||
|
elif isinstance(offset, basestring):
|
||||||
|
direction = 1
|
||||||
|
if offset[0] == "-":
|
||||||
|
direction = -1
|
||||||
|
offset = offset[1:]
|
||||||
|
if offset in ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']:
|
||||||
|
# next tuesday you come to, including this one
|
||||||
|
dow = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].index(offset)
|
||||||
|
ordinal = ndate.toordinal()
|
||||||
|
while ndate.fromordinal(ordinal).weekday() != dow:
|
||||||
|
ordinal += direction
|
||||||
|
ndate = ndate.fromordinal(ordinal)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
print "ndate:", ndate, "date:", date
|
||||||
|
|
||||||
|
if ndate == date:
|
||||||
|
if rule["if"] != "":
|
||||||
|
if not eval(rule["if"]):
|
||||||
|
continue
|
||||||
|
retval.append(rule["name"])
|
||||||
|
return retval
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Register the plugins
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
PluginManager.get_instance().register_plugin(
|
||||||
|
Plugin(
|
||||||
|
name = __name__,
|
||||||
|
description = _("Provides holiday information for different countries."),
|
||||||
|
module_name = __name__
|
||||||
|
)
|
||||||
|
)
|
488
src/plugins/textreport/BirthdayReport.py
Normal file
488
src/plugins/textreport/BirthdayReport.py
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
# Gramps - a GTK+/GNOME based genealogy program
|
||||||
|
#
|
||||||
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
||||||
|
# Copyright (C) 2008-2009 Brian G. Matherly
|
||||||
|
#
|
||||||
|
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# python modules
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
from gettext import gettext as _
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# GRAMPS modules
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
import BaseDoc
|
||||||
|
from BasicUtils import name_displayer
|
||||||
|
from gen.plug import PluginManager
|
||||||
|
from ReportBase import Report, ReportUtils, MenuReportOptions, CATEGORY_TEXT
|
||||||
|
from gen.plug.menu import BooleanOption, StringOption, NumberOption, \
|
||||||
|
EnumeratedListOption, FilterOption, PersonOption
|
||||||
|
import GrampsLocale
|
||||||
|
import gen.lib
|
||||||
|
from Utils import probably_alive, ProgressMeter
|
||||||
|
|
||||||
|
import libholiday
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Support functions
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
def make_date(year, month, day):
|
||||||
|
"""
|
||||||
|
Return a Date object of the particular year/month/day.
|
||||||
|
"""
|
||||||
|
retval = gen.lib.Date()
|
||||||
|
retval.set_yr_mon_day(year, month, day)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Calendar
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class CalendarReport(Report):
|
||||||
|
"""
|
||||||
|
Create the Calendar object that produces the report.
|
||||||
|
"""
|
||||||
|
def __init__(self, database, options_class):
|
||||||
|
Report.__init__(self, database, options_class)
|
||||||
|
menu = options_class.menu
|
||||||
|
|
||||||
|
self.titletext = menu.get_option_by_name('titletext').get_value()
|
||||||
|
self.relationships = \
|
||||||
|
menu.get_option_by_name('relationships').get_value()
|
||||||
|
self.year = menu.get_option_by_name('year').get_value()
|
||||||
|
self.name_format = menu.get_option_by_name('name_format').get_value()
|
||||||
|
self.country = menu.get_option_by_name('country').get_value()
|
||||||
|
self.anniversaries = menu.get_option_by_name('anniversaries').get_value()
|
||||||
|
self.start_dow = menu.get_option_by_name('start_dow').get_value()
|
||||||
|
self.maiden_name = menu.get_option_by_name('maiden_name').get_value()
|
||||||
|
self.alive = menu.get_option_by_name('alive').get_value()
|
||||||
|
self.birthdays = menu.get_option_by_name('birthdays').get_value()
|
||||||
|
self.text1 = menu.get_option_by_name('text1').get_value()
|
||||||
|
self.text2 = menu.get_option_by_name('text2').get_value()
|
||||||
|
self.text3 = menu.get_option_by_name('text3').get_value()
|
||||||
|
self.filter_option = menu.get_option_by_name('filter')
|
||||||
|
self.filter = self.filter_option.get_filter()
|
||||||
|
pid = menu.get_option_by_name('pid').get_value()
|
||||||
|
self.center_person = database.get_person_from_gramps_id(pid)
|
||||||
|
|
||||||
|
def get_name(self, person, maiden_name = None):
|
||||||
|
"""
|
||||||
|
Return person's name, unless maiden_name given, unless married_name
|
||||||
|
listed.
|
||||||
|
"""
|
||||||
|
# Get all of a person's names:
|
||||||
|
primary_name = person.get_primary_name()
|
||||||
|
married_name = None
|
||||||
|
names = [primary_name] + person.get_alternate_names()
|
||||||
|
for name in names:
|
||||||
|
if int(name.get_type()) == gen.lib.NameType.MARRIED:
|
||||||
|
married_name = name
|
||||||
|
break # use first
|
||||||
|
# Now, decide which to use:
|
||||||
|
if maiden_name is not None:
|
||||||
|
if married_name is not None:
|
||||||
|
name = gen.lib.Name(married_name)
|
||||||
|
else:
|
||||||
|
name = gen.lib.Name(primary_name)
|
||||||
|
name.set_surname(maiden_name)
|
||||||
|
else:
|
||||||
|
name = gen.lib.Name(primary_name)
|
||||||
|
name.set_display_as(self.name_format)
|
||||||
|
return name_displayer.display_name(name)
|
||||||
|
|
||||||
|
def add_day_item(self, text, month, day):
|
||||||
|
""" Add an item to a day. """
|
||||||
|
month_dict = self.calendar.get(month, {})
|
||||||
|
day_list = month_dict.get(day, [])
|
||||||
|
day_list.append(text)
|
||||||
|
month_dict[day] = day_list
|
||||||
|
self.calendar[month] = month_dict
|
||||||
|
|
||||||
|
def __get_holidays(self):
|
||||||
|
""" Get the holidays for the specified country and year """
|
||||||
|
holiday_table = libholiday.HolidayTable()
|
||||||
|
country = holiday_table.get_countries()[self.country]
|
||||||
|
holiday_table.load_holidays(self.year, country)
|
||||||
|
for month in range(1, 13):
|
||||||
|
for day in range(1, 32):
|
||||||
|
holiday_names = holiday_table.get_holidays(month, day)
|
||||||
|
for holiday_name in holiday_names:
|
||||||
|
self.add_day_item(holiday_name, month, day)
|
||||||
|
|
||||||
|
def write_report(self):
|
||||||
|
""" The short method that runs through each month and creates a page. """
|
||||||
|
# initialize the dict to fill:
|
||||||
|
self.progress = ProgressMeter(_('Birthday and Anniversary Report'))
|
||||||
|
self.calendar = {}
|
||||||
|
# get the information, first from holidays:
|
||||||
|
if self.country != 0:
|
||||||
|
self.__get_holidays()
|
||||||
|
# get data from database:
|
||||||
|
self.collect_data()
|
||||||
|
# generate the report:
|
||||||
|
self.doc.start_paragraph('BIR-Title')
|
||||||
|
self.doc.write_text(str(self.titletext) + ": " + str(self.year))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
if self.text1.strip() != "":
|
||||||
|
self.doc.start_paragraph('BIR-Text1style')
|
||||||
|
self.doc.write_text(str(self.text1))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
if self.text2.strip() != "":
|
||||||
|
self.doc.start_paragraph('BIR-Text2style')
|
||||||
|
self.doc.write_text(str(self.text2))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
if self.text3.strip() != "":
|
||||||
|
self.doc.start_paragraph('BIR-Text3style')
|
||||||
|
self.doc.write_text(str(self.text3))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
if self.relationships:
|
||||||
|
name = self.center_person.get_primary_name()
|
||||||
|
self.doc.start_paragraph('BIR-Text3style')
|
||||||
|
self.doc.write_text(_("Relationships shown are to %s") % name_displayer.display_name(name))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
self.progress.set_pass(_('Formatting months...'), 12)
|
||||||
|
for month in range(1, 13):
|
||||||
|
self.progress.step()
|
||||||
|
self.print_page(month)
|
||||||
|
self.progress.close()
|
||||||
|
|
||||||
|
def print_page(self, month):
|
||||||
|
""" Prints a month as a page """
|
||||||
|
year = self.year
|
||||||
|
self.doc.start_paragraph('BIR-Monthstyle')
|
||||||
|
self.doc.write_text(GrampsLocale.long_months[month].capitalize())
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
current_date = datetime.date(year, month, 1)
|
||||||
|
current_ord = current_date.toordinal()
|
||||||
|
started_day = {}
|
||||||
|
for i in range(31):
|
||||||
|
thisday = current_date.fromordinal(current_ord)
|
||||||
|
if thisday.month == month:
|
||||||
|
list = self.calendar.get(month, {}).get(thisday.day, [])
|
||||||
|
for p in list:
|
||||||
|
p = p.replace("\n", " ")
|
||||||
|
if thisday not in started_day:
|
||||||
|
self.doc.start_paragraph("BIR-Daystyle")
|
||||||
|
self.doc.write_text(str(thisday.day))
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
started_day[thisday] = 1
|
||||||
|
self.doc.start_paragraph("BIR-Datastyle")
|
||||||
|
self.doc.write_text(p)
|
||||||
|
self.doc.end_paragraph()
|
||||||
|
current_ord += 1
|
||||||
|
|
||||||
|
def collect_data(self):
|
||||||
|
"""
|
||||||
|
This method runs through the data, and collects the relevant dates
|
||||||
|
and text.
|
||||||
|
"""
|
||||||
|
people = self.database.get_person_handles(sort_handles=False)
|
||||||
|
self.progress.set_pass(_('Applying Filter...'), len(people))
|
||||||
|
people = self.filter.apply(self.database, people, self.progress)
|
||||||
|
pmgr = PluginManager.get_instance()
|
||||||
|
rel_calc = pmgr.get_relationship_calculator()
|
||||||
|
|
||||||
|
self.progress.set_pass(_('Reading database...'), len(people))
|
||||||
|
for person_handle in people:
|
||||||
|
self.progress.step()
|
||||||
|
person = self.database.get_person_from_handle(person_handle)
|
||||||
|
birth_ref = person.get_birth_ref()
|
||||||
|
birth_date = None
|
||||||
|
if birth_ref:
|
||||||
|
birth_event = self.database.get_event_from_handle(birth_ref.ref)
|
||||||
|
birth_date = birth_event.get_date_object()
|
||||||
|
if self.birthdays and birth_date is not None:
|
||||||
|
year = birth_date.get_year()
|
||||||
|
month = birth_date.get_month()
|
||||||
|
day = birth_date.get_day()
|
||||||
|
age = self.year - year
|
||||||
|
# add some things to handle maiden name:
|
||||||
|
father_lastname = None # husband, actually
|
||||||
|
if self.maiden_name in ['spouse_first', 'spouse_last']: # get husband's last name:
|
||||||
|
if person.get_gender() == gen.lib.Person.FEMALE:
|
||||||
|
family_list = person.get_family_handle_list()
|
||||||
|
if len(family_list) > 0:
|
||||||
|
if self.maiden_name == 'spouse_first':
|
||||||
|
fhandle = family_list[0]
|
||||||
|
else:
|
||||||
|
fhandle = family_list[-1]
|
||||||
|
fam = self.database.get_family_from_handle(fhandle)
|
||||||
|
father_handle = fam.get_father_handle()
|
||||||
|
mother_handle = fam.get_mother_handle()
|
||||||
|
if mother_handle == person_handle:
|
||||||
|
if father_handle:
|
||||||
|
father = self.database.get_person_from_handle(father_handle)
|
||||||
|
if father is not None:
|
||||||
|
father_lastname = father.get_primary_name().get_surname()
|
||||||
|
short_name = self.get_name(person, father_lastname)
|
||||||
|
if age >= 0:
|
||||||
|
alive = probably_alive(person, self.database, make_date(self.year, month, day))
|
||||||
|
if ((self.alive and alive) or not self.alive):
|
||||||
|
comment = ""
|
||||||
|
if self.relationships:
|
||||||
|
relation = rel_calc.get_one_relationship(
|
||||||
|
self.database,
|
||||||
|
self.center_person,
|
||||||
|
person)
|
||||||
|
if relation:
|
||||||
|
comment = " --- %s" % relation
|
||||||
|
self.add_day_item("%s, %d%s" % (short_name, age, comment), month, day)
|
||||||
|
if self.anniversaries:
|
||||||
|
family_list = person.get_family_handle_list()
|
||||||
|
for fhandle in family_list:
|
||||||
|
fam = self.database.get_family_from_handle(fhandle)
|
||||||
|
father_handle = fam.get_father_handle()
|
||||||
|
mother_handle = fam.get_mother_handle()
|
||||||
|
if father_handle == person.get_handle():
|
||||||
|
spouse_handle = mother_handle
|
||||||
|
else:
|
||||||
|
continue # with next person if the father is not "person"
|
||||||
|
# this will keep from duplicating the anniversary
|
||||||
|
if spouse_handle:
|
||||||
|
spouse = self.database.get_person_from_handle(spouse_handle)
|
||||||
|
if spouse:
|
||||||
|
spouse_name = self.get_name(spouse)
|
||||||
|
short_name = self.get_name(person)
|
||||||
|
# TEMP: this will hanlde ordered events
|
||||||
|
# GRAMPS 3.0 will have a new mechanism for start/stop events
|
||||||
|
are_married = None
|
||||||
|
for event_ref in fam.get_event_ref_list():
|
||||||
|
event = self.database.get_event_from_handle(event_ref.ref)
|
||||||
|
if event.type in [gen.lib.EventType.MARRIAGE,
|
||||||
|
gen.lib.EventType.MARR_ALT]:
|
||||||
|
are_married = event
|
||||||
|
elif event.type in [gen.lib.EventType.DIVORCE,
|
||||||
|
gen.lib.EventType.ANNULMENT,
|
||||||
|
gen.lib.EventType.DIV_FILING]:
|
||||||
|
are_married = None
|
||||||
|
if are_married is not None:
|
||||||
|
for event_ref in fam.get_event_ref_list():
|
||||||
|
event = self.database.get_event_from_handle(event_ref.ref)
|
||||||
|
event_obj = event.get_date_object()
|
||||||
|
year = event_obj.get_year()
|
||||||
|
month = event_obj.get_month()
|
||||||
|
day = event_obj.get_day()
|
||||||
|
years = self.year - year
|
||||||
|
if years >= 0:
|
||||||
|
text = _("%(spouse)s and\n %(person)s, %(nyears)d") % {
|
||||||
|
'spouse' : spouse_name,
|
||||||
|
'person' : short_name,
|
||||||
|
'nyears' : years,
|
||||||
|
}
|
||||||
|
alive1 = probably_alive(person, self.database, make_date(self.year, month, day))
|
||||||
|
alive2 = probably_alive(spouse, self.database, make_date(self.year, month, day))
|
||||||
|
if ((self.alive and alive1 and alive2) or not self.alive):
|
||||||
|
self.add_day_item(text, month, day)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CalendarOptions
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
class CalendarOptions(MenuReportOptions):
|
||||||
|
""" Options for the Birthday/Anniversary Report """
|
||||||
|
def __init__(self, name, dbase):
|
||||||
|
self.__db = dbase
|
||||||
|
self.__pid = None
|
||||||
|
self.__filter = None
|
||||||
|
MenuReportOptions.__init__(self, name, dbase)
|
||||||
|
|
||||||
|
def add_menu_options(self, menu):
|
||||||
|
""" Add the options for the graphical calendar """
|
||||||
|
category_name = _("Report Options")
|
||||||
|
|
||||||
|
year = NumberOption(_("Year of calendar"), time.localtime()[0],
|
||||||
|
1000, 3000)
|
||||||
|
year.set_help(_("Year of calendar"))
|
||||||
|
menu.add_option(category_name, "year", year)
|
||||||
|
|
||||||
|
self.__filter = FilterOption(_("Filter"), 0)
|
||||||
|
self.__filter.set_help(
|
||||||
|
_("Select filter to restrict people that appear on calendar"))
|
||||||
|
menu.add_option(category_name, "filter", self.__filter)
|
||||||
|
|
||||||
|
self.__pid = PersonOption(_("Center Person"))
|
||||||
|
self.__pid.set_help(_("The center person for the report"))
|
||||||
|
menu.add_option(category_name, "pid", self.__pid)
|
||||||
|
self.__pid.connect('value-changed', self.__update_filters)
|
||||||
|
|
||||||
|
self.__update_filters()
|
||||||
|
|
||||||
|
# We must figure out the value of the first option before we can
|
||||||
|
# create the EnumeratedListOption
|
||||||
|
fmt_list = name_displayer.get_name_format()
|
||||||
|
name_format = EnumeratedListOption(_("Name format"), fmt_list[0][0])
|
||||||
|
for num, name, fmt_str, act in fmt_list:
|
||||||
|
name_format.add_item(num, name)
|
||||||
|
name_format.set_help(_("Select the format to display names"))
|
||||||
|
menu.add_option(category_name, "name_format", name_format)
|
||||||
|
|
||||||
|
country = EnumeratedListOption(_("Country for holidays"), 0)
|
||||||
|
holiday_table = libholiday.HolidayTable()
|
||||||
|
count = 0
|
||||||
|
for c in holiday_table.get_countries():
|
||||||
|
country.add_item(count, c)
|
||||||
|
count += 1
|
||||||
|
country.set_help(_("Select the country to see associated holidays"))
|
||||||
|
menu.add_option(category_name, "country", country)
|
||||||
|
|
||||||
|
start_dow = EnumeratedListOption(_("First day of week"), 1)
|
||||||
|
for count in range(1, 8):
|
||||||
|
# conversion between gramps numbering (sun=1) and iso numbering (mon=1) of weekdays below
|
||||||
|
start_dow.add_item((count+5) % 7 + 1, GrampsLocale.long_days[count].capitalize())
|
||||||
|
start_dow.set_help(_("Select the first day of the week for the calendar"))
|
||||||
|
menu.add_option(category_name, "start_dow", start_dow)
|
||||||
|
|
||||||
|
maiden_name = EnumeratedListOption(_("Birthday surname"), "own")
|
||||||
|
maiden_name.add_item("spouse_first", _("Wives use husband's surname (from first family listed)"))
|
||||||
|
maiden_name.add_item("spouse_last", _("Wives use husband's surname (from last family listed)"))
|
||||||
|
maiden_name.add_item("own", _("Wives use their own surname"))
|
||||||
|
maiden_name.set_help(_("Select married women's displayed surname"))
|
||||||
|
menu.add_option(category_name, "maiden_name", maiden_name)
|
||||||
|
|
||||||
|
alive = BooleanOption(_("Include only living people"), True)
|
||||||
|
alive.set_help(_("Include only living people in the calendar"))
|
||||||
|
menu.add_option(category_name, "alive", alive)
|
||||||
|
|
||||||
|
birthdays = BooleanOption(_("Include birthdays"), True)
|
||||||
|
birthdays.set_help(_("Include birthdays in the calendar"))
|
||||||
|
menu.add_option(category_name, "birthdays", birthdays)
|
||||||
|
|
||||||
|
anniversaries = BooleanOption(_("Include anniversaries"), True)
|
||||||
|
anniversaries.set_help(_("Include anniversaries in the calendar"))
|
||||||
|
menu.add_option(category_name, "anniversaries", anniversaries)
|
||||||
|
|
||||||
|
option = BooleanOption(_("Include relationships to center person"),
|
||||||
|
False)
|
||||||
|
option.set_help(_("Include relationships to center person (slower)"))
|
||||||
|
menu.add_option(category_name, "relationships", option)
|
||||||
|
|
||||||
|
category_name = _("Text Options")
|
||||||
|
|
||||||
|
titletext = StringOption(_("Title text"),
|
||||||
|
_("Birthday and Anniversary Report"))
|
||||||
|
titletext.set_help(_("Title of calendar"))
|
||||||
|
menu.add_option(category_name, "titletext", titletext)
|
||||||
|
|
||||||
|
text1 = StringOption(_("Text Area 1"), _("My Calendar"))
|
||||||
|
text1.set_help(_("First line of text at bottom of calendar"))
|
||||||
|
menu.add_option(category_name, "text1", text1)
|
||||||
|
|
||||||
|
text2 = StringOption(_("Text Area 2"), _("Produced with GRAMPS"))
|
||||||
|
text2.set_help(_("Second line of text at bottom of calendar"))
|
||||||
|
menu.add_option(category_name, "text2", text2)
|
||||||
|
|
||||||
|
text3 = StringOption(_("Text Area 3"), "http://gramps-project.org/",)
|
||||||
|
text3.set_help(_("Third line of text at bottom of calendar"))
|
||||||
|
menu.add_option(category_name, "text3", text3)
|
||||||
|
|
||||||
|
def __update_filters(self):
|
||||||
|
"""
|
||||||
|
Update the filter list based on the selected person
|
||||||
|
"""
|
||||||
|
gid = self.__pid.get_value()
|
||||||
|
person = self.__db.get_person_from_gramps_id(gid)
|
||||||
|
filter_list = ReportUtils.get_person_filters(person, False)
|
||||||
|
self.__filter.set_filters(filter_list)
|
||||||
|
|
||||||
|
def make_my_style(self, default_style, name, description,
|
||||||
|
size=9, font=BaseDoc.FONT_SERIF, justified ="left",
|
||||||
|
color=None, align=BaseDoc.PARA_ALIGN_CENTER,
|
||||||
|
shadow = None, italic=0, bold=0, borders=0, indent=None):
|
||||||
|
""" Create paragraph and graphic styles of the same name """
|
||||||
|
# Paragraph:
|
||||||
|
f = BaseDoc.FontStyle()
|
||||||
|
f.set_size(size)
|
||||||
|
f.set_type_face(font)
|
||||||
|
f.set_italic(italic)
|
||||||
|
f.set_bold(bold)
|
||||||
|
p = BaseDoc.ParagraphStyle()
|
||||||
|
p.set_font(f)
|
||||||
|
p.set_alignment(align)
|
||||||
|
p.set_description(description)
|
||||||
|
p.set_top_border(borders)
|
||||||
|
p.set_left_border(borders)
|
||||||
|
p.set_bottom_border(borders)
|
||||||
|
p.set_right_border(borders)
|
||||||
|
if indent:
|
||||||
|
p.set(first_indent=indent)
|
||||||
|
if justified == "left":
|
||||||
|
p.set_alignment(BaseDoc.PARA_ALIGN_LEFT)
|
||||||
|
elif justified == "right":
|
||||||
|
p.set_alignment(BaseDoc.PARA_ALIGN_RIGHT)
|
||||||
|
elif justified == "center":
|
||||||
|
p.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
|
||||||
|
default_style.add_paragraph_style(name, p)
|
||||||
|
# Graphics:
|
||||||
|
g = BaseDoc.GraphicsStyle()
|
||||||
|
g.set_paragraph_style(name)
|
||||||
|
if shadow:
|
||||||
|
g.set_shadow(*shadow)
|
||||||
|
if color is not None:
|
||||||
|
g.set_fill_color(color)
|
||||||
|
if not borders:
|
||||||
|
g.set_line_width(0)
|
||||||
|
default_style.add_draw_style(name, g)
|
||||||
|
|
||||||
|
def make_default_style(self, default_style):
|
||||||
|
""" Add the styles used in this report """
|
||||||
|
self.make_my_style(default_style, "BIR-Title",
|
||||||
|
_('Title text style'), 14,
|
||||||
|
bold=1, justified="center")
|
||||||
|
self.make_my_style(default_style, "BIR-Datastyle",
|
||||||
|
_('Data text display'), 12, indent=1.0)
|
||||||
|
self.make_my_style(default_style, "BIR-Daystyle",
|
||||||
|
_('Day text style'), 12, indent=.5,
|
||||||
|
italic=1, bold=1)
|
||||||
|
self.make_my_style(default_style, "BIR-Monthstyle",
|
||||||
|
_('Month text style'), 14, bold=1)
|
||||||
|
self.make_my_style(default_style, "BIR-Text1style",
|
||||||
|
_('Text at bottom, line 1'), 12, justified="center")
|
||||||
|
self.make_my_style(default_style, "BIR-Text2style",
|
||||||
|
_('Text at bottom, line 2'), 12, justified="center")
|
||||||
|
self.make_my_style(default_style, "BIR-Text3style",
|
||||||
|
_('Text at bottom, line 3'), 12, justified="center")
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Register the plugins
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------
|
||||||
|
pmgr = PluginManager.get_instance()
|
||||||
|
pmgr.register_report(
|
||||||
|
name = 'birthday_report',
|
||||||
|
category = CATEGORY_TEXT,
|
||||||
|
report_class = CalendarReport,
|
||||||
|
options_class = CalendarOptions,
|
||||||
|
modes = PluginManager.REPORT_MODE_GUI | \
|
||||||
|
PluginManager.REPORT_MODE_BKI | \
|
||||||
|
PluginManager.REPORT_MODE_CLI,
|
||||||
|
translated_name = _("Birthday and Anniversary Report"),
|
||||||
|
status = _("Stable"),
|
||||||
|
author_name = "Douglas S. Blank",
|
||||||
|
author_email = "dblank@cs.brynmawr.edu",
|
||||||
|
description = _("Produces a report of birthdays and anniversaries"),
|
||||||
|
)
|
Reference in New Issue
Block a user