2006-03-01 01:24:35 +05:30
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
2007-09-16 09:21:18 +05:30
|
|
|
# Copyright (C) 2000-2007 Donald N. Allingham
|
2006-03-01 01:24:35 +05:30
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
2007-09-16 09:21:18 +05:30
|
|
|
__author__ = "Douglas Blank <dblank@cs.brynmawr.edu>"
|
2006-03-01 01:24:35 +05:30
|
|
|
__version__ = "$Revision$"
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# python modules
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2006-04-07 03:32:46 +05:30
|
|
|
from gettext import gettext as _
|
2006-03-01 01:24:35 +05:30
|
|
|
from xml.parsers import expat
|
2007-01-25 07:13:31 +05:30
|
|
|
import datetime
|
2007-10-31 20:00:54 +05:30
|
|
|
import math
|
2007-01-25 07:13:31 +05:30
|
|
|
import time
|
2006-03-01 01:24:35 +05:30
|
|
|
import const
|
|
|
|
import os
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# GRAMPS modules
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
import BaseDoc
|
2007-09-16 09:21:18 +05:30
|
|
|
from BasicUtils import name_displayer
|
2007-10-26 05:52:12 +05:30
|
|
|
from DateHandler import displayer
|
2007-12-15 22:39:44 +05:30
|
|
|
from PluginUtils import register_report, relationship_class
|
2007-11-29 20:32:40 +05:30
|
|
|
from ReportBase import Report, ReportUtils, MenuReportOptions, \
|
2007-11-29 05:03:40 +05:30
|
|
|
CATEGORY_DRAW, CATEGORY_TEXT, \
|
|
|
|
MODE_GUI, MODE_BKI, MODE_CLI
|
2007-11-29 20:32:40 +05:30
|
|
|
from PluginUtils import NumberOption, BooleanOption, StringOption, \
|
2008-01-06 02:12:05 +05:30
|
|
|
PersonFilterOption, EnumeratedListOption
|
2006-05-15 09:43:31 +05:30
|
|
|
import GrampsLocale
|
2007-10-08 22:11:39 +05:30
|
|
|
import gen.lib
|
2007-09-16 09:21:18 +05:30
|
|
|
from Utils import probably_alive, ProgressMeter
|
2007-01-26 20:36:52 +05:30
|
|
|
from FontScale import string_trim, string_width
|
2007-09-16 09:21:18 +05:30
|
|
|
pt2cm = ReportUtils.pt2cm
|
|
|
|
cm2pt = ReportUtils.cm2pt
|
2006-03-01 01:24:35 +05:30
|
|
|
|
2007-10-16 07:56:38 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# 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 )
|
2007-10-26 05:52:12 +05:30
|
|
|
return "%d/%d/%d" % (year, month, day)
|
2007-10-16 07:56:38 +05:30
|
|
|
|
|
|
|
def g2iso(dow):
|
|
|
|
""" Converst GRAMPS day of week to ISO day of week """
|
|
|
|
# GRAMPS: SUN = 1
|
|
|
|
# ISO: MON = 1
|
|
|
|
return (dow + 5) % 7 + 1
|
|
|
|
|
2007-10-31 20:00:54 +05:30
|
|
|
def dst(year, area="us"):
|
|
|
|
"""
|
|
|
|
Returns 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)
|
|
|
|
|
2007-11-21 20:19:50 +05:30
|
|
|
def make_date(year, month, day):
|
|
|
|
"""
|
|
|
|
Returns a Date object of the particular year/month/day.
|
|
|
|
"""
|
|
|
|
retval = gen.lib.Date()
|
|
|
|
retval.set_yr_mon_day(year, month, day)
|
|
|
|
return retval
|
|
|
|
|
2006-03-01 01:24:35 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Calendar
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2006-06-01 10:09:40 +05:30
|
|
|
class Calendar(Report):
|
2006-03-01 01:24:35 +05:30
|
|
|
"""
|
|
|
|
Creates the Calendar object that produces the report.
|
|
|
|
"""
|
2007-09-16 09:21:18 +05:30
|
|
|
def __init__(self,database,person,options_class):
|
|
|
|
Report.__init__(self,database,person,options_class)
|
2007-10-14 07:26:00 +05:30
|
|
|
if 'titletext' in options_class.handler.options_dict.keys():
|
|
|
|
# report and graphic share most of the same code
|
|
|
|
# but calendar doesn't have a title
|
|
|
|
self.titletext = options_class.handler.options_dict['titletext']
|
2007-12-15 22:39:44 +05:30
|
|
|
if 'relationships' in options_class.handler.options_dict.keys():
|
|
|
|
# report and graphic share most of the same code
|
|
|
|
# but calendar doesn't show relationships
|
|
|
|
self.relationships = options_class.handler.options_dict['relationships']
|
|
|
|
else:
|
|
|
|
self.relationships = False
|
2007-09-16 09:21:18 +05:30
|
|
|
self.year = options_class.handler.options_dict['year']
|
2007-12-16 23:57:43 +05:30
|
|
|
self.name_format = options_class.handler.options_dict['name_format']
|
2007-09-16 09:21:18 +05:30
|
|
|
self.country = options_class.handler.options_dict['country']
|
|
|
|
self.anniversaries = options_class.handler.options_dict['anniversaries']
|
2007-12-16 23:57:43 +05:30
|
|
|
self.start_dow = options_class.handler.options_dict['start_dow']
|
|
|
|
self.maiden_name = options_class.handler.options_dict['maiden_name']
|
2007-09-16 09:21:18 +05:30
|
|
|
self.alive = options_class.handler.options_dict['alive']
|
|
|
|
self.birthdays = options_class.handler.options_dict['birthdays']
|
2007-10-14 07:26:00 +05:30
|
|
|
self.text1 = options_class.handler.options_dict['text1']
|
|
|
|
self.text2 = options_class.handler.options_dict['text2']
|
|
|
|
self.text3 = options_class.handler.options_dict['text3']
|
2007-12-15 22:39:44 +05:30
|
|
|
self.filter_option = options_class.menu.get_option_by_name('filter')
|
2007-12-17 10:39:34 +05:30
|
|
|
self.filter = self.filter_option.get_filter()
|
2007-12-17 09:42:01 +05:30
|
|
|
|
2007-12-15 22:39:44 +05:30
|
|
|
self.title = _("Calendar Report") #% name
|
2006-03-01 01:24:35 +05:30
|
|
|
|
2007-10-17 07:02:51 +05:30
|
|
|
def get_name(self, person, maiden_name = None):
|
2006-12-25 10:53:06 +05:30
|
|
|
""" Returns 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 n in names:
|
2007-10-08 22:11:39 +05:30
|
|
|
if int(n.get_type()) == gen.lib.NameType.MARRIED:
|
2006-12-25 10:53:06 +05:30
|
|
|
married_name = n
|
2007-10-22 02:11:49 +05:30
|
|
|
break # use first
|
2006-12-25 10:53:06 +05:30
|
|
|
# Now, decide which to use:
|
|
|
|
if maiden_name != None:
|
|
|
|
if married_name != None:
|
2007-10-17 07:02:51 +05:30
|
|
|
name = gen.lib.Name(married_name)
|
2006-12-25 10:53:06 +05:30
|
|
|
else:
|
2007-10-17 07:02:51 +05:30
|
|
|
name = gen.lib.Name(primary_name)
|
|
|
|
name.set_surname(maiden_name)
|
2006-03-01 01:24:35 +05:30
|
|
|
else:
|
2007-10-17 07:02:51 +05:30
|
|
|
name = gen.lib.Name(primary_name)
|
|
|
|
name.set_display_as(self.name_format)
|
|
|
|
return name_displayer.display_name(name)
|
2006-03-01 01:24:35 +05:30
|
|
|
|
|
|
|
def draw_rectangle(self, style, sx, sy, ex, ey):
|
|
|
|
""" This should be in BaseDoc """
|
|
|
|
self.doc.draw_line(style, sx, sy, sx, ey)
|
|
|
|
self.doc.draw_line(style, sx, sy, ex, sy)
|
|
|
|
self.doc.draw_line(style, ex, sy, ex, ey)
|
|
|
|
self.doc.draw_line(style, sx, ey, ex, ey)
|
|
|
|
|
|
|
|
### The rest of these all have to deal with calendar specific things
|
|
|
|
|
|
|
|
def add_day_item(self, text, year, month, day):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Adds an item to a day. """
|
2006-03-01 01:24:35 +05:30
|
|
|
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
|
|
|
|
|
2007-01-25 07:13:31 +05:30
|
|
|
def get_holidays(self, year, country = "United States"):
|
2006-03-01 01:24:35 +05:30
|
|
|
""" Looks in multiple places for holidays.xml files """
|
2007-11-26 10:41:19 +05:30
|
|
|
locations = [const.PLUGINS_DIR, const.USER_PLUGINS]
|
2006-03-01 01:24:35 +05:30
|
|
|
holiday_file = 'holidays.xml'
|
|
|
|
for dir in locations:
|
|
|
|
holiday_full_path = os.path.join(dir, holiday_file)
|
|
|
|
if os.path.exists(holiday_full_path):
|
|
|
|
self.process_holiday_file(holiday_full_path, year, country)
|
|
|
|
|
|
|
|
def process_holiday_file(self, filename, year, country):
|
|
|
|
""" This will process a holiday file """
|
|
|
|
parser = Xml2Obj()
|
|
|
|
element = parser.Parse(filename)
|
|
|
|
calendar = Holidays(element, country)
|
|
|
|
date = datetime.date(year, 1, 1)
|
|
|
|
while date.year == year:
|
|
|
|
holidays = calendar.check_date( date )
|
|
|
|
for text in holidays:
|
|
|
|
self.add_day_item(text, date.year, date.month, date.day)
|
|
|
|
date = date.fromordinal( date.toordinal() + 1)
|
|
|
|
|
|
|
|
def write_report(self):
|
|
|
|
""" The short method that runs through each month and creates a page. """
|
|
|
|
# initialize the dict to fill:
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress = ProgressMeter(_('Calendar'))
|
2006-03-01 01:24:35 +05:30
|
|
|
self.calendar = {}
|
|
|
|
# get the information, first from holidays:
|
2007-12-16 23:57:43 +05:30
|
|
|
if self.country != 0: # Don't include holidays
|
|
|
|
self.get_holidays(self.year, _countries[self.country]) # _countries is currently global
|
2006-03-01 01:24:35 +05:30
|
|
|
# get data from database:
|
|
|
|
self.collect_data()
|
|
|
|
# generate the report:
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.set_pass(_('Formating months...'), 12)
|
2006-03-01 01:24:35 +05:30
|
|
|
for month in range(1, 13):
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.step()
|
2006-03-01 01:24:35 +05:30
|
|
|
self.print_page(month)
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.close()
|
2006-03-01 01:24:35 +05:30
|
|
|
|
|
|
|
def print_page(self, month):
|
|
|
|
"""
|
|
|
|
This method actually writes the calendar page.
|
|
|
|
"""
|
2007-09-16 09:21:18 +05:30
|
|
|
style_sheet = self.doc.get_style_sheet()
|
|
|
|
ptitle = style_sheet.get_paragraph_style("CAL-Title")
|
|
|
|
ptext = style_sheet.get_paragraph_style("CAL-Text")
|
|
|
|
pdaynames = style_sheet.get_paragraph_style("CAL-Daynames")
|
|
|
|
pnumbers = style_sheet.get_paragraph_style("CAL-Numbers")
|
|
|
|
ptext1style = style_sheet.get_paragraph_style("CAL-Text1style")
|
|
|
|
|
2006-03-01 01:24:35 +05:30
|
|
|
self.doc.start_page()
|
|
|
|
width = self.doc.get_usable_width()
|
|
|
|
height = self.doc.get_usable_height()
|
2007-02-19 09:43:41 +05:30
|
|
|
header = 2.54 # one inch
|
2007-01-24 20:21:32 +05:30
|
|
|
self.draw_rectangle("CAL-Border", 0, 0, width, height)
|
2007-02-27 09:40:43 +05:30
|
|
|
self.doc.draw_box("CAL-Title", "", 0, 0, width, header)
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.draw_line("CAL-Border", 0, header, width, header)
|
2007-09-16 09:21:18 +05:30
|
|
|
year = self.year
|
2007-10-31 20:00:54 +05:30
|
|
|
title = "%s %d" % (GrampsLocale.long_months[month].capitalize(), year)
|
2007-09-16 09:21:18 +05:30
|
|
|
font_height = pt2cm(ptitle.get_font().get_size())
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.center_text("CAL-Title", title, width/2, font_height * 0.25)
|
2006-03-01 01:24:35 +05:30
|
|
|
cell_width = width / 7
|
|
|
|
cell_height = (height - header)/ 6
|
|
|
|
current_date = datetime.date(year, month, 1)
|
2007-09-16 09:21:18 +05:30
|
|
|
spacing = pt2cm(1.25 * ptext.get_font().get_size()) # 158
|
2007-10-16 07:56:38 +05:30
|
|
|
if current_date.isoweekday() != g2iso(self.start_dow + 1):
|
2007-10-05 18:25:08 +05:30
|
|
|
# Go back to previous first day of week, and start from there
|
2007-10-16 07:56:38 +05:30
|
|
|
current_ord = (current_date.toordinal() -
|
|
|
|
((current_date.isoweekday() + 7) -
|
|
|
|
g2iso(self.start_dow + 1) ) % 7)
|
2006-03-01 01:24:35 +05:30
|
|
|
else:
|
|
|
|
current_ord = current_date.toordinal()
|
|
|
|
for day_col in range(7):
|
2007-09-16 09:21:18 +05:30
|
|
|
font_height = pt2cm(pdaynames.get_font().get_size())
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.center_text("CAL-Daynames",
|
2007-10-16 07:56:38 +05:30
|
|
|
GrampsLocale.long_days[(day_col+
|
|
|
|
g2iso(self.start_dow + 1))
|
2007-10-31 20:00:54 +05:30
|
|
|
% 7 + 1].capitalize(),
|
2006-03-01 01:24:35 +05:30
|
|
|
day_col * cell_width + cell_width/2,
|
2007-01-24 20:21:32 +05:30
|
|
|
header - font_height * 1.5)
|
2006-03-01 01:24:35 +05:30
|
|
|
for week_row in range(6):
|
|
|
|
something_this_week = 0
|
|
|
|
for day_col in range(7):
|
|
|
|
thisday = current_date.fromordinal(current_ord)
|
|
|
|
if thisday.month == month:
|
|
|
|
something_this_week = 1
|
2007-01-24 20:21:32 +05:30
|
|
|
self.draw_rectangle("CAL-Border", day_col * cell_width,
|
2006-03-01 01:24:35 +05:30
|
|
|
header + week_row * cell_height,
|
|
|
|
(day_col + 1) * cell_width,
|
|
|
|
header + (week_row + 1) * cell_height)
|
|
|
|
last_edge = (day_col + 1) * cell_width
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.center_text("CAL-Numbers", str(thisday.day),
|
2006-03-01 01:24:35 +05:30
|
|
|
day_col * cell_width + cell_width/2,
|
2007-01-24 20:21:32 +05:30
|
|
|
header + week_row * cell_height)
|
2006-03-01 01:24:35 +05:30
|
|
|
list = self.calendar.get(month, {}).get(thisday.day, [])
|
|
|
|
position = 0.0
|
|
|
|
for p in list:
|
|
|
|
lines = p.count("\n") + 1 # lines in the text
|
|
|
|
position += (lines * spacing)
|
|
|
|
current = 0
|
|
|
|
for line in p.split("\n"):
|
2007-01-26 20:36:52 +05:30
|
|
|
# make sure text will fit:
|
2007-09-16 09:21:18 +05:30
|
|
|
numpos = pt2cm(pnumbers.get_font().get_size())
|
2007-01-26 20:36:52 +05:30
|
|
|
if position + (current * spacing) - 0.1 >= cell_height - numpos: # font daynums
|
|
|
|
continue
|
2007-09-16 09:21:18 +05:30
|
|
|
font = ptext.get_font()
|
2007-01-26 20:36:52 +05:30
|
|
|
line = string_trim(font, line, cm2pt(cell_width + 0.2))
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.draw_text("CAL-Text", line,
|
2006-12-25 10:53:06 +05:30
|
|
|
day_col * cell_width + 0.1,
|
|
|
|
header + (week_row + 1) * cell_height - position + (current * spacing) - 0.1)
|
2006-03-01 01:24:35 +05:30
|
|
|
current += 1
|
|
|
|
current_ord += 1
|
|
|
|
if not something_this_week:
|
|
|
|
last_edge = 0
|
2007-09-16 09:21:18 +05:30
|
|
|
font_height = pt2cm(1.5 * ptext1style.get_font().get_size())
|
|
|
|
self.doc.center_text("CAL-Text1style", self.text1, last_edge + (width - last_edge)/2, height - font_height * 3)
|
|
|
|
self.doc.center_text("CAL-Text2style", self.text2, last_edge + (width - last_edge)/2, height - font_height * 2)
|
|
|
|
self.doc.center_text("CAL-Text3style", self.text3, last_edge + (width - last_edge)/2, height - font_height * 1)
|
2006-03-01 01:24:35 +05:30
|
|
|
self.doc.end_page()
|
|
|
|
|
|
|
|
def collect_data(self):
|
|
|
|
"""
|
|
|
|
This method runs through the data, and collects the relevant dates
|
|
|
|
and text.
|
|
|
|
"""
|
2007-12-15 22:39:44 +05:30
|
|
|
self.progress.set_pass(_('Filtering data...'), 0)
|
2006-03-01 01:24:35 +05:30
|
|
|
people = self.filter.apply(self.database,
|
|
|
|
self.database.get_person_handles(sort_handles=False))
|
2007-12-15 22:39:44 +05:30
|
|
|
center_person = self.filter_option.get_center_person()
|
|
|
|
rel_calc = relationship_class()
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.set_pass(_('Filtering data...'), len(people))
|
2006-03-01 01:24:35 +05:30
|
|
|
for person_handle in people:
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.step()
|
2006-03-01 01:24:35 +05:30
|
|
|
person = self.database.get_person_from_handle(person_handle)
|
2006-03-31 09:49:06 +05:30
|
|
|
birth_ref = person.get_birth_ref()
|
2006-03-01 01:24:35 +05:30
|
|
|
birth_date = None
|
2006-03-31 09:49:06 +05:30
|
|
|
if birth_ref:
|
|
|
|
birth_event = self.database.get_event_from_handle(birth_ref.ref)
|
|
|
|
birth_date = birth_event.get_date_object()
|
2007-11-21 20:19:50 +05:30
|
|
|
if self.birthdays and birth_date != None:
|
2006-03-31 09:49:06 +05:30
|
|
|
year = birth_date.get_year()
|
|
|
|
month = birth_date.get_month()
|
|
|
|
day = birth_date.get_day()
|
2007-09-16 09:21:18 +05:30
|
|
|
age = self.year - year
|
2006-03-01 01:24:35 +05:30
|
|
|
# add some things to handle maiden name:
|
|
|
|
father_lastname = None # husband, actually
|
2007-10-22 02:11:49 +05:30
|
|
|
if self.maiden_name in ['spouse_first', 'spouse_last']: # get husband's last name:
|
2007-10-08 22:11:39 +05:30
|
|
|
if person.get_gender() == gen.lib.Person.FEMALE:
|
2006-03-01 01:24:35 +05:30
|
|
|
family_list = person.get_family_handle_list()
|
|
|
|
if len(family_list) > 0:
|
2007-10-22 02:11:49 +05:30
|
|
|
if self.maiden_name == 'spouse_first':
|
|
|
|
fhandle = family_list[0]
|
|
|
|
else:
|
|
|
|
fhandle = family_list[-1]
|
2006-03-01 01:24:35 +05:30
|
|
|
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 != None:
|
|
|
|
father_lastname = father.get_primary_name().get_surname()
|
2007-10-17 07:02:51 +05:30
|
|
|
short_name = self.get_name(person, father_lastname)
|
2007-10-14 07:26:00 +05:30
|
|
|
if age >= 0:
|
2007-11-21 20:19:50 +05:30
|
|
|
alive = probably_alive(person, self.database, make_date(self.year, month, day))
|
|
|
|
if ((self.alive and alive) or not self.alive):
|
2007-12-15 22:39:44 +05:30
|
|
|
comment = ""
|
|
|
|
if self.relationships:
|
|
|
|
relation = rel_calc.get_one_relationship(self.database, center_person, person)
|
|
|
|
if relation:
|
|
|
|
comment = " --- %s" % relation
|
|
|
|
self.add_day_item("%s, %d%s" % (short_name, age, comment), self.year, month, day)
|
2007-11-21 20:19:50 +05:30
|
|
|
if self.anniversaries:
|
2006-03-01 01:24:35 +05:30
|
|
|
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:
|
2007-10-22 02:11:49 +05:30
|
|
|
continue # with next person if the father is not "person"
|
|
|
|
# this will keep from duplicating the anniversary
|
2006-03-01 01:24:35 +05:30
|
|
|
if spouse_handle:
|
|
|
|
spouse = self.database.get_person_from_handle(spouse_handle)
|
|
|
|
if spouse:
|
2007-10-17 07:02:51 +05:30
|
|
|
spouse_name = self.get_name(spouse)
|
|
|
|
short_name = self.get_name(person)
|
2007-10-16 07:56:38 +05:30
|
|
|
# TEMP: this will hanlde ordered events
|
|
|
|
# GRAMPS 3.0 will have a new mechanism for start/stop events
|
|
|
|
are_married = None
|
2006-03-31 09:49:06 +05:30
|
|
|
for event_ref in fam.get_event_ref_list():
|
|
|
|
event = self.database.get_event_from_handle(event_ref.ref)
|
2007-10-16 07:56:38 +05:30
|
|
|
if int(event.get_type()) in [gen.lib.EventType.MARRIAGE,
|
|
|
|
gen.lib.EventType.MARR_ALT]:
|
|
|
|
are_married = event
|
|
|
|
elif int(event.get_type()) in [gen.lib.EventType.DIVORCE,
|
|
|
|
gen.lib.EventType.ANNULMENT,
|
|
|
|
gen.lib.EventType.DIV_FILING]:
|
|
|
|
are_married = None
|
|
|
|
if are_married != 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,
|
|
|
|
}
|
2007-11-21 20:19:50 +05:30
|
|
|
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, self.year, month, day)
|
|
|
|
|
2007-01-23 08:59:59 +05:30
|
|
|
class CalendarReport(Calendar):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" The Calendar text report """
|
2007-01-23 08:59:59 +05:30
|
|
|
def write_report(self):
|
|
|
|
""" The short method that runs through each month and creates a page. """
|
|
|
|
# initialize the dict to fill:
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress = ProgressMeter(_('Birthday and Anniversary Report'))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.calendar = {}
|
|
|
|
# get the information, first from holidays:
|
2007-12-16 23:57:43 +05:30
|
|
|
if self.country != 0:
|
|
|
|
self.get_holidays(self.year, _countries[self.country]) # _countries currently global
|
2007-01-23 08:59:59 +05:30
|
|
|
# get data from database:
|
|
|
|
self.collect_data()
|
|
|
|
# generate the report:
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph('BIR-Title')
|
2007-09-16 09:21:18 +05:30
|
|
|
self.doc.write_text(str(self.titletext) + ": " + str(self.year))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.end_paragraph()
|
2007-09-16 09:21:18 +05:30
|
|
|
if self.text1.strip() != "":
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph('BIR-Text1style')
|
2007-09-16 09:21:18 +05:30
|
|
|
self.doc.write_text(str(self.text1))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.end_paragraph()
|
2007-09-16 09:21:18 +05:30
|
|
|
if self.text2.strip() != "":
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph('BIR-Text2style')
|
2007-09-16 09:21:18 +05:30
|
|
|
self.doc.write_text(str(self.text2))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.end_paragraph()
|
2007-09-16 09:21:18 +05:30
|
|
|
if self.text3.strip() != "":
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph('BIR-Text3style')
|
2007-09-16 09:21:18 +05:30
|
|
|
self.doc.write_text(str(self.text3))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.end_paragraph()
|
2007-12-15 22:39:44 +05:30
|
|
|
if self.relationships:
|
|
|
|
center_person = self.filter_option.get_center_person()
|
|
|
|
name = 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()
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.set_pass(_('Formating months...'), 12)
|
2007-01-23 08:59:59 +05:30
|
|
|
for month in range(1, 13):
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.step()
|
2007-01-23 08:59:59 +05:30
|
|
|
self.print_page(month)
|
2007-09-16 09:21:18 +05:30
|
|
|
self.progress.close()
|
|
|
|
|
2007-01-23 08:59:59 +05:30
|
|
|
def print_page(self, month):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Prints a month as a page """
|
|
|
|
year = self.year
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph('BIR-Monthstyle')
|
2007-10-31 20:00:54 +05:30
|
|
|
self.doc.write_text(GrampsLocale.long_months[month].capitalize())
|
2007-01-23 08:59:59 +05:30
|
|
|
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:
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph("BIR-Daystyle")
|
2007-10-31 06:43:53 +05:30
|
|
|
self.doc.write_text(str(thisday.day))
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.end_paragraph()
|
|
|
|
started_day[thisday] = 1
|
2007-01-24 20:21:32 +05:30
|
|
|
self.doc.start_paragraph("BIR-Datastyle")
|
2007-01-23 08:59:59 +05:30
|
|
|
self.doc.write_text(p)
|
|
|
|
self.doc.end_paragraph()
|
|
|
|
current_ord += 1
|
|
|
|
|
2007-11-29 05:03:40 +05:30
|
|
|
class CalendarOptions(MenuReportOptions):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Calendar options for graphic calendar """
|
|
|
|
|
2007-12-30 11:11:16 +05:30
|
|
|
def add_menu_options(self, menu,dbstate):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Adds the options for the graphical calendar """
|
2007-10-14 07:26:00 +05:30
|
|
|
category_name = _("Report Options")
|
2007-09-16 09:21:18 +05:30
|
|
|
|
|
|
|
year = NumberOption(_("Year of calendar"), time.localtime()[0], 1000, 3000)
|
|
|
|
year.set_help(_("Year of calendar"))
|
|
|
|
menu.add_option(category_name,"year", year)
|
|
|
|
|
2008-01-06 02:12:05 +05:30
|
|
|
filter = PersonFilterOption(_("Filter"),dbstate,0,False)
|
2007-09-16 09:21:18 +05:30
|
|
|
filter.set_help(_("Select filter to restrict people that appear on calendar"))
|
|
|
|
menu.add_option(category_name,"filter", filter)
|
|
|
|
|
2007-12-17 10:39:34 +05:30
|
|
|
name_format = EnumeratedListOption(_("Name format"), -1)
|
2007-10-17 07:02:51 +05:30
|
|
|
for num, name, fmt_str, act in name_displayer.get_name_format():
|
|
|
|
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)
|
|
|
|
|
2007-12-17 10:39:34 +05:30
|
|
|
country = EnumeratedListOption(_("Country for holidays"), 0)
|
2007-09-16 09:21:18 +05:30
|
|
|
count = 0
|
|
|
|
for c in _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)
|
|
|
|
|
2007-12-17 10:39:34 +05:30
|
|
|
start_dow = EnumeratedListOption(_("First day of week"), 1)
|
2007-10-05 18:25:08 +05:30
|
|
|
for count in range(1,8):
|
|
|
|
# conversion between gramps numbering (sun=1) and iso numbering (mon=1) of weekdays below
|
2007-10-31 20:00:54 +05:30
|
|
|
start_dow.add_item((count+5) % 7 + 1, GrampsLocale.long_days[count].capitalize())
|
2007-10-05 18:25:08 +05:30
|
|
|
start_dow.set_help(_("Select the first day of the week for the calendar"))
|
|
|
|
menu.add_option(category_name, "start_dow", start_dow)
|
|
|
|
|
2007-12-17 10:39:34 +05:30
|
|
|
maiden_name = EnumeratedListOption(_("Birthday surname"), "own")
|
2007-10-22 02:11:49 +05:30
|
|
|
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)"))
|
2007-10-17 07:02:51 +05:30
|
|
|
maiden_name.add_item("own", _("Wives use their own surname"))
|
2007-09-16 09:21:18 +05:30
|
|
|
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)
|
|
|
|
|
2007-10-14 07:26:00 +05:30
|
|
|
category_name = _("Text Options")
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
2007-09-16 09:21:18 +05:30
|
|
|
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):
|
|
|
|
""" Creates paragraph and graphic styles of the same name """
|
|
|
|
# Paragraph:
|
2006-03-01 01:24:35 +05:30
|
|
|
f = BaseDoc.FontStyle()
|
2007-09-16 09:21:18 +05:30
|
|
|
f.set_size(size)
|
|
|
|
f.set_type_face(font)
|
|
|
|
f.set_italic(italic)
|
|
|
|
f.set_bold(bold)
|
2006-03-01 01:24:35 +05:30
|
|
|
p = BaseDoc.ParagraphStyle()
|
|
|
|
p.set_font(f)
|
2007-09-16 09:21:18 +05:30
|
|
|
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":
|
2007-01-24 20:21:32 +05:30
|
|
|
p.set_alignment(BaseDoc.PARA_ALIGN_LEFT)
|
2007-09-16 09:21:18 +05:30
|
|
|
elif justified == "right":
|
2007-01-24 20:21:32 +05:30
|
|
|
p.set_alignment(BaseDoc.PARA_ALIGN_RIGHT)
|
2007-09-16 09:21:18 +05:30
|
|
|
elif justified == "center":
|
2007-01-24 20:21:32 +05:30
|
|
|
p.set_alignment(BaseDoc.PARA_ALIGN_CENTER)
|
2007-09-16 09:21:18 +05:30
|
|
|
default_style.add_paragraph_style(name,p)
|
|
|
|
# Graphics:
|
2006-03-01 01:24:35 +05:30
|
|
|
g = BaseDoc.GraphicsStyle()
|
2007-09-16 09:21:18 +05:30
|
|
|
g.set_paragraph_style(name)
|
|
|
|
if shadow:
|
|
|
|
g.set_shadow(*shadow)
|
|
|
|
if color != None:
|
|
|
|
g.set_fill_color(color)
|
|
|
|
if not borders:
|
|
|
|
g.set_line_width(0)
|
|
|
|
default_style.add_draw_style(name,g)
|
2006-03-01 01:24:35 +05:30
|
|
|
|
2007-09-16 09:21:18 +05:30
|
|
|
def make_default_style(self, default_style):
|
|
|
|
""" Adds the styles used in this report """
|
|
|
|
self.make_my_style(default_style, "CAL-Title",
|
|
|
|
_('Title text and background color'), 20,
|
|
|
|
bold=1, italic=1,
|
|
|
|
color=(0xEA,0xEA,0xEA))
|
|
|
|
self.make_my_style(default_style, "CAL-Numbers",
|
|
|
|
_('Calendar day numbers'), 13,
|
|
|
|
bold=1)
|
|
|
|
self.make_my_style(default_style, "CAL-Text",
|
|
|
|
_('Daily text display'), 9)
|
|
|
|
self.make_my_style(default_style,"CAL-Daynames",
|
|
|
|
_('Days of the week text'), 12,
|
|
|
|
italic=1, bold=1,
|
|
|
|
color = (0xEA,0xEA,0xEA))
|
|
|
|
self.make_my_style(default_style,"CAL-Text1style",
|
|
|
|
_('Text at bottom, line 1'), 12)
|
|
|
|
self.make_my_style(default_style,"CAL-Text2style",
|
|
|
|
_('Text at bottom, line 2'), 12)
|
|
|
|
self.make_my_style(default_style,"CAL-Text3style",
|
|
|
|
_('Text at bottom, line 3'), 9)
|
|
|
|
self.make_my_style(default_style,"CAL-Border",
|
|
|
|
_('Borders'), borders=True)
|
|
|
|
|
|
|
|
class CalendarReportOptions(CalendarOptions):
|
|
|
|
""" Options for the calendar (birthday and anniversary) report """
|
2007-10-14 07:26:00 +05:30
|
|
|
|
2007-12-30 11:11:16 +05:30
|
|
|
def add_menu_options(self, menu,dbstate):
|
2007-10-14 07:26:00 +05:30
|
|
|
""" Adds the options for the graphical calendar """
|
|
|
|
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)
|
2007-12-30 11:11:16 +05:30
|
|
|
CalendarOptions.add_menu_options(self, menu, dbstate)
|
2007-12-15 22:39:44 +05:30
|
|
|
category_name = _("Report Options")
|
|
|
|
option = BooleanOption(_("Include relationships to center person (slower)"),
|
|
|
|
False)
|
|
|
|
option.set_help(_("Include relationships to center person"))
|
|
|
|
menu.add_option(category_name,"relationships", option)
|
2007-10-14 07:26:00 +05:30
|
|
|
|
2007-09-16 09:21:18 +05:30
|
|
|
def make_default_style(self, default_style):
|
|
|
|
""" Adds the options for the textual 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",
|
2007-10-26 05:52:12 +05:30
|
|
|
_('Month text style'), 14, bold=1)
|
2007-09-16 09:21:18 +05:30
|
|
|
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")
|
2007-01-23 08:59:59 +05:30
|
|
|
|
2007-10-26 05:52:12 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# XML Classes
|
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
2006-03-01 01:24:35 +05:30
|
|
|
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):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Converts item at level to a XML string """
|
2006-03-01 01:24:35 +05:30
|
|
|
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):
|
2008-01-11 12:20:46 +05:30
|
|
|
'Create a SAX parser and parse filename '
|
2006-03-01 01:24:35 +05:30
|
|
|
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):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Set the contry of holidays to read """
|
2006-03-01 01:24:35 +05:30
|
|
|
self.country = country
|
|
|
|
self.dates = []
|
|
|
|
self.initialize()
|
|
|
|
def initialize(self):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Parse the holiday date XML items """
|
2006-03-01 01:24:35 +05:30
|
|
|
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):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Get the items for a particular year/month and day of week """
|
2006-03-01 01:24:35 +05:30
|
|
|
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):
|
2007-09-16 09:21:18 +05:30
|
|
|
""" Returns items that match rules """
|
2006-03-01 01:24:35 +05:30
|
|
|
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"]
|
2007-10-17 09:51:34 +05:30
|
|
|
if len(rule["value"]) > 0 and rule["value"][0] == '>':
|
2007-10-16 07:56:38 +05:30
|
|
|
# eval exp -> year/num[/day[/month]]
|
|
|
|
y, m, d = date.year, date.month, date.day
|
|
|
|
rule["value"] = eval(rule["value"][1:])
|
2007-10-17 09:51:34 +05:30
|
|
|
if self.debug: print "rule['value']:", rule["value"]
|
2006-03-01 01:24:35 +05:30
|
|
|
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 type(offset) == int:
|
|
|
|
if offset != 0:
|
|
|
|
ndate = ndate.fromordinal(ndate.toordinal() + offset)
|
|
|
|
elif type(offset) in [type(u''), str]:
|
|
|
|
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
|
|
|
|
|
2007-01-25 07:13:31 +05:30
|
|
|
def get_countries():
|
|
|
|
""" Looks in multiple places for holidays.xml files """
|
2007-11-26 10:41:19 +05:30
|
|
|
locations = [const.PLUGINS_DIR, const.USER_PLUGINS]
|
2007-01-25 07:13:31 +05:30
|
|
|
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 = process_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
|
|
|
|
|
|
|
|
def process_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
|
|
|
|
|
|
|
|
## Currently reads the XML file on load. Could move this someplace else
|
|
|
|
## so it only loads when needed.
|
|
|
|
|
|
|
|
_countries = get_countries()
|
|
|
|
|
2006-03-01 01:24:35 +05:30
|
|
|
#------------------------------------------------------------------------
|
|
|
|
#
|
2007-01-25 07:13:31 +05:30
|
|
|
# Register the plugins
|
2006-03-01 01:24:35 +05:30
|
|
|
#
|
|
|
|
#------------------------------------------------------------------------
|
|
|
|
register_report(
|
|
|
|
name = 'calendar',
|
2006-06-01 10:09:40 +05:30
|
|
|
category = CATEGORY_DRAW,
|
2006-03-01 01:24:35 +05:30
|
|
|
report_class = Calendar,
|
|
|
|
options_class = CalendarOptions,
|
2006-06-01 10:09:40 +05:30
|
|
|
modes = MODE_GUI | MODE_BKI | MODE_CLI,
|
2006-03-01 01:24:35 +05:30
|
|
|
translated_name = _("Calendar"),
|
2007-01-23 08:59:59 +05:30
|
|
|
status = _("Stable"),
|
2006-03-01 01:24:35 +05:30
|
|
|
author_name = "Douglas S. Blank",
|
2007-01-23 08:59:59 +05:30
|
|
|
author_email = "dblank@cs.brynmawr.edu",
|
2006-03-01 01:24:35 +05:30
|
|
|
description = _("Produces a graphical calendar"),
|
|
|
|
)
|
2007-09-16 09:21:18 +05:30
|
|
|
|
2007-01-23 08:59:59 +05:30
|
|
|
register_report(
|
|
|
|
name = 'birthday_report',
|
|
|
|
category = CATEGORY_TEXT,
|
|
|
|
report_class = CalendarReport,
|
|
|
|
options_class = CalendarReportOptions,
|
|
|
|
modes = MODE_GUI | MODE_BKI | 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"),
|
|
|
|
)
|