# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007 Thom Sturgill # Copyright (C) 2007-2008 Brian G. Matherly # # 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 # 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 # # $Id$ """ Web Calendar generator. 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. Reports/Web Page/Web Calendar... """ #------------------------------------------------------------------------ # # python modules # #------------------------------------------------------------------------ import os import time import datetime #import const import codecs import shutil from gettext import gettext as _ from xml.parsers import expat try: set() except: from sets import Set as set #------------------------------------------------------------------------ # # Set up logging # #------------------------------------------------------------------------ import logging log = logging.getLogger(".WebPage") #------------------------------------------------------------------------ # # GRAMPS module # #------------------------------------------------------------------------ import gen.lib import const import BaseDoc from GrampsCfg import get_researcher from PluginUtils import register_report from ReportBase import (Report, ReportUtils, MenuReportOptions, CATEGORY_WEB, MODE_GUI) from PluginUtils import FilterOption, EnumeratedListOption, PersonOption, \ BooleanOption, NumberOption, StringOption, DestinationOption import Utils import GrampsLocale from QuestionDialog import ErrorDialog from Utils import probably_alive #------------------------------------------------------------------------ # # constants # #------------------------------------------------------------------------ _CALENDAR = "calendar.css" _CHARACTER_SETS = [ [_('Unicode (recommended)'), 'utf-8'], ['ISO-8859-1', 'iso-8859-1' ], ['ISO-8859-2', 'iso-8859-2' ], ['ISO-8859-3', 'iso-8859-3' ], ['ISO-8859-4', 'iso-8859-4' ], ['ISO-8859-5', 'iso-8859-5' ], ['ISO-8859-6', 'iso-8859-6' ], ['ISO-8859-7', 'iso-8859-7' ], ['ISO-8859-8', 'iso-8859-8' ], ['ISO-8859-9', 'iso-8859-9' ], ['ISO-8859-10', 'iso-8859-10' ], ['ISO-8859-13', 'iso-8859-13' ], ['ISO-8859-14', 'iso-8859-14' ], ['ISO-8859-15', 'iso-8859-15' ], ['koi8_r', 'koi8_r', ], ] _CC = [ '' 'Creative Commons License - By attribution', '' 'Creative Commons License - By attribution, No derivations', '' 'Creative Commons License - By attribution, Share-alike', '' 'Creative Commons License - By attribution, Non-commercial', '' 'Creative Commons License - By attribution, Non-commercial, No '
    'derivations', '' 'Creative Commons License - By attribution, Non-commerical, '
    'Share-alike' ] _COPY_OPTIONS = [ _('Standard copyright'), _('Creative Commons - By attribution'), _('Creative Commons - By attribution, No derivations'), _('Creative Commons - By attribution, Share-alike'), _('Creative Commons - By attribution, Non-commercial'), _('Creative Commons - By attribution, Non-commercial, No derivations'), _('Creative Commons - By attribution, Non-commercial, Share-alike'), _('No copyright notice'), ] 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 #------------------------------------------------------------------------ # # WebCalReport # #------------------------------------------------------------------------ class WebCalReport(Report): """ Creates WebCalReport object that produces the report. """ def __init__(self, database, options): Report.__init__(self, database, options) menu = options.menu self.database = database self.options = options self.html_dir = menu.get_option_by_name('target').get_value() filter_option = menu.get_option_by_name('filter') self.filter = filter_option.get_filter() self.ext = menu.get_option_by_name('ext').get_value() self.copy = menu.get_option_by_name('cright').get_value() self.encoding = menu.get_option_by_name('encoding').get_value() self.Country = menu.get_option_by_name('country').get_value() self.Year = menu.get_option_by_name('year').get_value() self.Surname = menu.get_option_by_name('surname').get_value() self.Alive = menu.get_option_by_name('alive').get_value() self.Birthday = menu.get_option_by_name('birthdays').get_value() self.Anniv = menu.get_option_by_name('anniversaries').get_value() self.Title_text = menu.get_option_by_name('title').get_value() self.Month_image = menu.get_option_by_name('background').get_value() self.Month_repeat = menu.get_option_by_name('repeat').get_value() self.Serif_fonts = menu.get_option_by_name('serif_fonts').get_value() self.SanSerif_fonts = \ menu.get_option_by_name('sanserif_fonts').get_value() self.Home_link = menu.get_option_by_name('home_link').get_value() self.Note = [ menu.get_option_by_name('note_jan').get_value(), menu.get_option_by_name('note_feb').get_value(), menu.get_option_by_name('note_mar').get_value(), menu.get_option_by_name('note_apr').get_value(), menu.get_option_by_name('note_may').get_value(), menu.get_option_by_name('note_jun').get_value(), menu.get_option_by_name('note_jul').get_value(), menu.get_option_by_name('note_aug').get_value(), menu.get_option_by_name('note_sep').get_value(), menu.get_option_by_name('note_oct').get_value(), menu.get_option_by_name('note_nov').get_value(), menu.get_option_by_name('note_dec').get_value()] def get_short_name(self, person, maiden_name = None): """ 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: if int(n.get_type()) == gen.lib.NameType.MARRIED: married_name = n # Now, decide which to use: if maiden_name != None: if married_name != None: first_name, family_name = married_name.get_first_name(), married_name.get_surname() call_name = married_name.get_call_name() else: first_name, family_name = primary_name.get_first_name(), maiden_name call_name = primary_name.get_call_name() else: first_name, family_name = primary_name.get_first_name(), primary_name.get_surname() call_name = primary_name.get_call_name() # If they have a nickname use it if call_name != None and call_name.strip() != "": first_name = call_name.strip() else: # else just get the first name: first_name = first_name.strip() if " " in first_name: first_name, rest = first_name.split(" ", 1) # just one split max return ("%s %s" % (first_name, family_name)).strip() def add_day_item(self, text, year, month, 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, year, country = "United States"): """ Looks in multiple places for holidays.xml files """ locations = [const.PLUGINS_DIR, const.USER_PLUGINS] 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_css(self): """ Create the CSS file. """ # simplify the style and weight printing font_style = ['normal','italic'] font_weight = ['normal','bold'] # use user defined font families font_family = [self.SanSerif_fonts,self.Serif_fonts] default_style = BaseDoc.StyleSheet() self.options.make_default_style(default_style) # Read all style sheets available for this item style_file = self.options.handler.get_stylesheet_savefile() self.style_list = BaseDoc.StyleSheetList(style_file,default_style) # Get the selected stylesheet style_name = self.options.handler.get_default_stylesheet_name() self.selected_style = self.style_list.get_style_sheet(style_name) default_style = BaseDoc.StyleSheet(self.selected_style) # # NAVIGATION BLOCK # of = self.create_file(_CALENDAR) of.write('ul#navlist { padding: 0;\n\tmargin: 0;\n\tlist-style-type: none;') of.write('\n\tfloat: left;\n\twidth: 100%;') of.write('\n\tcolor: #FFFFFF;\n\tbackground-color: #003366;\n\t}\n') of.write('ul#navlist li { display: inline; }\n') of.write('ul#navlist li a { float: left;\n\twidth: 2.8em;') of.write('\n\tcolor: #FFFFFF;\n\tbackground-color: #003366;') of.write('\n\tpadding: 0.2em 1em;\n\ttext-decoration: none;') of.write('\n\tborder-right: 1px solid #FFFFFF;\n\t}\n') of.write('ul#navlist li a:hover { background-color: #336699;') of.write('\n\tcolor: #FFFFFF;\n\t}\n') # # HEADER / BODY BACKGROUND # of.write('h1 {') style = default_style.get_paragraph_style("WC-Title") font = style.get_font() italic = font_style[font.get_italic()] bold = font_weight[font.get_bold()] family = font_family[font.get_type_face()] color = "#%02X%02X%02X" % font.get_color() of.write('\tfont-family: %s;\n\tfont-size: %dpt;\n' '\tfont-style: %s;\n\tfont-weight: %s;\n' '\tcolor: %s;\n\ttext-align: %s;\n\t}\n' % (family, font.get_size(), italic, bold, color, style.get_alignment_text())) of.write('body { background-color: #%02X%02X%02X;\n}\n' % style.get_background_color() ) # # CALENDAR TABLE # of.write('.calendar { ') style = default_style.get_paragraph_style("WC-Table") font = style.get_font() italic = font_style[font.get_italic()] bold = font_weight[font.get_bold()] family = font_family[font.get_type_face()] color = "#%02X%02X%02X" % font.get_color() of.write('font-family: %s;\n\tfont-size: %dpt;\n' '\tfont-style: %s;\n\tfont-weight: %s;\n' '\tcolor: %s;\n\ttext-align: %s;\n' % (family, font.get_size(), italic, bold, color, style.get_alignment_text())) of.write('\tbackground-color: #%02X%02X%02X;\n}\n' % style.get_background_color() ) # # MONTH NAME # style = default_style.get_paragraph_style("WC-Month") of.write('.cal_month { border-bottom-width: 0;\n') font = style.get_font() italic = font_style[font.get_italic()] bold = font_weight[font.get_bold()] family = font_family[font.get_type_face()] color = "#%02X%02X%02X" % font.get_color() mon_backcolor = "#%02X%02X%02X" % style.get_background_color() of.write('\tfont-family:%s;\n\tfont-size: %dpt;\n' '\tfont-style: %s;\n\tfont-weight: %s;\n' '\tcolor: %s;\n\ttext-align: %s;\n' % (family, font.get_size(), italic, bold, color, style.get_alignment_text())) if self.Month_image.strip() != "": of.write('\tbackground-image: URL( %s );\n' % self.Month_image) of.write('\tbackground-repeat: %s;\n' % self.options.repeat_options[self.Month_repeat] ) of.write('\tbackground-color: %s;\n}\n' % mon_backcolor ) # # WEEKDAY NAMES # of.write('.cal_sun { border-top-width: 0;\n\tborder-right-width: 0;') of.write('\n\tborder-style: solid; ') of.write('background-color: %s }\n' % mon_backcolor ) of.write('.cal_weekday { border-top-width: 0;\n\tborder-left-width: 0;\n\tborder-right-width: 0; ') of.write('\n\tborder-style: solid;\n\tbackground-color: %s }\n' % mon_backcolor ) of.write('.cal_sat { border-top-width: 0;\n\tborder-left-width: 0;\n') of.write('\tborder-right-width: 0;\n\tborder-style: solid;') of.write('\n\tbackground-color: %s }\n' % mon_backcolor ) #of.write('.cal_day_num { text-align: right;\n\tfont-size: x-large;\n\tfont-weight: bold;}\n') # # CALENDAR ENTRY TEXT # style = default_style.get_paragraph_style("WC-Text") of.write('.cal_text { vertical-align:bottom;\n') font = style.get_font() italic = font_style[font.get_italic()] bold = font_weight[font.get_bold()] family = font_family[font.get_type_face()] color = "#%02X%02X%02X" % font.get_color() msg_backcolor = "#%02X%02X%02X" % style.get_background_color() of.write('\tfont-family:%s;\n\tfont-size: %dpt;\n' '\tfont-style: %s;\n\tfont-weight: %s;\n' '\tcolor: %s;\n\ttext-align: %s;\n\t}\n' % (family, font.get_size(), italic, bold, color, style.get_alignment_text())) of.write('.cal_row { height: 70px;\n\tborder-style: solid;\n\t}\n') of.write('.cal_cell_hilite {background-color: %s;}\n' % msg_backcolor ) # # CALENDAR NOTE TEXT # style = default_style.get_paragraph_style("WC-Note") font = style.get_font() italic = font_style[font.get_italic()] bold = font_weight[font.get_bold()] family = font_family[font.get_type_face()] color = "#%02X%02X%02X" % font.get_color() backcolor = "#%02X%02X%02X" % style.get_background_color() of.write('.cal_cell { background-color: %s;}\n' % backcolor ) of.write('.cal_note {\n') of.write('\tfont-family:%s;\n\tfont-size: %dpt;\n' '\tfont-style: %s;\n\tfont-weight: %s;\n' '\tcolor: %s;\n\ttext-align: %s;\n' '\tbackground-color: %s;\n\t}\n' % (family, font.get_size(), italic, bold, color, style.get_alignment_text(), backcolor)) # # FOOTER AND DONE # of.write('.footer { text-align: center;\n\tfont-size:small; }\n') of.write('img { border: 0; }\n') of.close() def write_footer(self,of): author = get_researcher().get_name() value = unicode(time.strftime('%x',time.localtime(time.time())), GrampsLocale.codeset) msg = _('Generated by ' 'GRAMPS on %(date)s') % { 'date' : value } of.write(' \n') of.write(' \n') of.write(' \n') of.write('\n') def write_header(self,of): author = get_researcher().get_name() of.write('\n') #xmllang = Utils.xml_lang() of.write('\n') of.write('\n %s\n' % self.Title_text) of.write(' \n' % self.encoding) of.write(' \n') of.write(' \n') of.write(' \n' % author) of.write(' \n') of.write(' \n') #of.write('\n' % ('$','$')) of.write('\n') of.write('\n') of.write(' \n') def create_file(self,name): page_name = os.path.join(self.html_dir,name) of = codecs.EncodedFile(open(page_name, "w"),'utf-8',self.encoding,'xmlcharrefreplace') return of def close_file(self,of): of.close() def write_report(self): """ The short method that runs through each month and creates a page. """ if not os.path.isdir(self.html_dir): parent_dir = os.path.dirname(self.html_dir) if not os.path.isdir(parent_dir): ErrorDialog(_("Neither %s nor %s are directories") % \ (self.html_dir,parent_dir)) return else: try: os.mkdir(self.html_dir) except IOError, value: ErrorDialog(_("Could not create the directory: %s") % \ self.html_dir + "\n" + value[1]) return except: ErrorDialog(_("Could not create the directory: %s") % \ self.html_dir) return # initialize the dict to fill: self.calendar = {} self.progress = Utils.ProgressMeter(_("Generate HTML Calendars"),'') # Generate the CSS file self.write_css() # get the information, first from holidays: if self.Country != 0: # Don't include holidays self.get_holidays(self.Year, _COUNTRIES[self.Country]) # _country is currently global # get data from database: self.collect_data() # generate the report: self.progress.set_pass(_("Creating Calendar pages"),12) for month in range(1, 13): self.progress.step() self.print_page(month) self.progress.close() def print_page(self, month): """ This method actually writes the calendar page. """ year = self.Year title = "%s %d" % (GrampsLocale.long_months[month], year) cal_file = "Calendar_%s%d.html" % (GrampsLocale.short_months[month], year) of = self.create_file(cal_file) self.write_header(of) of.write('

%s

\n' % self.Title_text) of.write('\n') of.write(' \n \n \n' % title) current_date = datetime.date(year, month, 1) if current_date.isoweekday() != 7: # start dow here is 7, sunday current_ord = current_date.toordinal() - current_date.isoweekday() else: current_ord = current_date.toordinal() of.write(' \n') for day_col in range(5): of.write(' \n') of.write(' \n \n') for week_row in range(6): first = True last = True of.write(' \n') something_this_week = 0 colspan = 0 for day_col in range(7): colspan += 1 thisday = current_date.fromordinal(current_ord) if thisday.month == month: list = self.calendar.get(month, {}).get(thisday.day, []) if list > []: cellclass = "cal_cell_hilite" else: cellclass = "cal_cell" if first: first = False if day_col > 1: of.write(' \n' % str(day_col)) elif day_col == 1: of.write(' \n') of.write(' \n') else: # at bottom of calendar if thisday.month > month and thisday.year >= year: # only do it once per row if last: last = False of.write(' \n') continue if week_row == 5: of.write(' class="cal_note">') if self.Note[month-1].strip() != '': of.write(self.Note[month-1]) else: of.write(" ") of.write('\n') continue of.write('\n') continue #of.write('\n') current_ord += 1 if week_row == 5 and month == 12: of.write(' \n') of.write(' \n') self.write_footer(of) self.close_file(of) def collect_data(self): """ This method runs through the data, and collects the relevant dates and text. """ people = self.filter.apply(self.database, self.database.get_person_handles(sort_handles=False)) for person_handle in people: 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() living = probably_alive(person, self.database, make_date(self.Year, 1, 1), 0) if self.Birthday and birth_date != None and ((self.Alive and living) or not self.Alive): 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.Surname == 0: # 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: fhandle = family_list[0] # first is primary 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() short_name = self.get_short_name(person, father_lastname) self.add_day_item("%s, %d" % (short_name, age), year, month, day) if self.Anniv and ((self.Alive and living) or not self.Alive): 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 this was the marriage event if spouse_handle: spouse = self.database.get_person_from_handle(spouse_handle) if spouse: spouse_name = self.get_short_name(spouse) short_name = self.get_short_name(person) if self.Alive: if not probably_alive(spouse, self.database, make_date(self.Year, 1, 1), 0): continue married = True for event_ref in fam.get_event_ref_list(): event = self.database.get_event_from_handle(event_ref.ref) if event and int(event.get_type()) in [gen.lib.EventType.DIVORCE, gen.lib.EventType.ANNULMENT, gen.lib.EventType.DIV_FILING]: married = False if married: 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 text = _("%(spouse)s and\n %(person)s, %(nyears)d") % { 'spouse' : spouse_name, 'person' : short_name, 'nyears' : years, } self.add_day_item(text, year, month, day) #------------------------------------------------------------------------ # # WebCalOptions # #------------------------------------------------------------------------ class WebCalOptions(MenuReportOptions): """ Defines options and provides handling interface. """ 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 options to the menu for the web calendar. """ self.__add_report_options(menu) self.__add_content_options(menu) self.__add_misc_options(menu) self.__add_notes_options(menu) def __add_report_options(self, menu): """ Options on the "Report Options" tab. """ category_name = _("Report Options") target = DestinationOption( _("Destination"), os.path.join(const.USER_HOME,"WEBCAL")) target.set_help( _("The destination directory for the web files")) target.set_directory_entry(True) menu.add_option(category_name, "target", target) ext = EnumeratedListOption(_("File extension"), ".html" ) for etype in ['.html', '.htm', '.shtml', '.php', '.php3', '.cgi']: ext.add_item(etype, etype) ext.set_help( _("The extension to be used for the web files")) menu.add_option(category_name, "ext", ext) cright = EnumeratedListOption(_('Copyright'), 0 ) index = 0 for copt in _COPY_OPTIONS: cright.add_item(index, copt) index += 1 cright.set_help( _("The copyright to be used for the web files")) menu.add_option(category_name, "cright", cright) encoding = EnumeratedListOption(_('Character set encoding'), 'utf-8' ) for eopt in _CHARACTER_SETS: encoding.add_item(eopt[1], eopt[0]) encoding.set_help( _("The encoding to be used for the web files")) menu.add_option(category_name, "encoding", encoding) def __add_content_options(self, menu): """ Options on the "Content Options" tab. """ category_name = _("Content 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.__filter.connect('value-changed', self.__filter_changed) self.__pid = PersonOption(_("Filter Person")) self.__pid.set_help(_("The center person for the filter")) menu.add_option(category_name, "pid", self.__pid) self.__pid.connect('value-changed', self.__update_filters) self.__update_filters() country = EnumeratedListOption(_('Country for holidays'), 0 ) index = 0 for item in _COUNTRIES: country.add_item(index, item) index += 1 country.set_help( _("Holidays will be included for the selected " "country")) menu.add_option(category_name, "country", country) 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) surname = BooleanOption(_('Check for wives to use maiden name'), True) surname.set_help(_("Attempt to use maiden names of women")) menu.add_option(category_name, "surname", surname) def __add_misc_options(self, menu): """ Options on the "Misc Options" tab. """ category_name = _("Misc Options") title = StringOption(_('Calendar Title'), _('My Family Calendar')) title.set_help(_("The title of the calendar")) menu.add_option(category_name, "title", title) home_link = StringOption(_('Home link'), '../index.html') home_link.set_help(_("The link to be included to direct the user to " "the main page of the web site")) menu.add_option(category_name, "home_link", home_link) serif_fonts = StringOption(_('Serif font family'), '"Georgia","Times New Roman","Times",serif') serif_fonts.set_help(_("Serif font family")) menu.add_option(category_name, "serif_fonts", serif_fonts) sanserif_fonts = StringOption(_('San-Serif font family'), '"Verdana","Helvetica","Arial",sans-serif') sanserif_fonts.set_help(_('San-Serif font family')) menu.add_option(category_name, "sanserif_fonts", sanserif_fonts) background = StringOption(_('Background Image'), "") background.set_help(_('The image to be used as the page background')) menu.add_option(category_name, "background", background) repeat = EnumeratedListOption(_('Image Repeat'), 1 ) repeat.add_item(0, _('no-repeat')) repeat.add_item(1, _('repeat')) repeat.add_item(2, _('repeat-x')) repeat.add_item(3, _('repeat-y')) repeat.set_help( _("Whether to repeat the background image")) menu.add_option(category_name, "repeat", repeat) def __add_notes_options(self, menu): """ Options on the "Months Notes" tabs. """ category_name = _("Months 1-6 Notes") note_jan = StringOption(_('Jan Note'), _('This prints in January')) note_jan.set_help(_("The note for the month of January")) menu.add_option(category_name, "note_jan", note_jan) note_feb = StringOption(_('Feb Note'), _('This prints in February')) note_feb.set_help(_("The note for the month of February")) menu.add_option(category_name, "note_feb", note_feb) note_mar = StringOption(_('Mar Note'), _('This prints in March')) note_mar.set_help(_("The note for the month of March")) menu.add_option(category_name, "note_mar", note_mar) note_apr = StringOption(_('Apr Note'), _('This prints in April')) note_apr.set_help(_("The note for the month of April")) menu.add_option(category_name, "note_apr", note_apr) note_may = StringOption(_('May Note'), _('This prints in May')) note_may.set_help(_("The note for the month of May")) menu.add_option(category_name, "note_may", note_may) note_jun = StringOption(_('Jun Note'), _('This prints in June')) note_jun.set_help(_("The note for the month of June")) menu.add_option(category_name, "note_jun", note_jun) category_name = _("Months 7-12 Notes") note_jul = StringOption(_('Jul Note'), _('This prints in July')) note_jul.set_help(_("The note for the month of July")) menu.add_option(category_name, "note_jul", note_jul) note_aug = StringOption(_('Aug Note'), _('This prints in August')) note_aug.set_help(_("The note for the month of August")) menu.add_option(category_name, "note_aug", note_aug) note_sep = StringOption(_('Sep Note'), _('This prints in September')) note_sep.set_help(_("The note for the month of September")) menu.add_option(category_name, "note_sep", note_sep) note_oct = StringOption(_('Oct Note'), _('This prints in October')) note_oct.set_help(_("The note for the month of October")) menu.add_option(category_name, "note_oct", note_oct) note_nov = StringOption(_('Nov Note'), _('This prints in November')) note_nov.set_help(_("The note for the month of November")) menu.add_option(category_name, "note_nov", note_nov) note_dec = StringOption(_('Dec Note'), _('This prints in December')) note_dec.set_help(_("The note for the month of December")) menu.add_option(category_name, "note_dec", note_dec) 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 __filter_changed(self): """ Handle filter change. If the filter is not specific to a person, disable the person option """ filter_value = self.__filter.get_value() if filter_value in [1, 2, 3, 4]: # Filters 1, 2, 3 and 4 rely on the center person self.__pid.set_available(True) else: # The rest don't self.__pid.set_available(False) def make_default_style(self, default_style): """Make the default output style for the Web Calendar There are 5 named styles for this report. WC-Title - The header for the page. WC-Month - The Month name block for the calendar. WC-Text - The text format for the body of the calendar. WC-Note - The text placed at the bottom of each calendar. WC-Table - controls the overall appearance of the calendar table. """ # # WC-Title # font = BaseDoc.FontStyle() font.set(face=BaseDoc.FONT_SERIF, size=24, bold=1, italic=1, color=(0x80, 0x0, 0x0)) para = BaseDoc.ParagraphStyle() para.set_font(font) para.set(bgcolor=((0xb0, 0xc4, 0xde))) para.set_alignment(BaseDoc.PARA_ALIGN_CENTER) para.set_description(_('The style used for the title ("My Family ' 'Calendar") of the page. The background color ' 'sets the PAGE background. Borders DO NOT ' 'work.')) default_style.add_paragraph_style("WC-Title", para) # # WC-Month # font = BaseDoc.FontStyle() font.set(face=BaseDoc.FONT_SERIF, size=48, bold=1, italic=1, color=((0x80, 0x0, 0x0))) para = BaseDoc.ParagraphStyle() para.set_font(font) para.set(bgcolor=((0xf0, 0xe6, 0x8c))) para.set_alignment(BaseDoc.PARA_ALIGN_CENTER) para.set_description(_('The style used for the month name and year, it' ' controls the font face, size, style, color ' 'and the background color of the block, ' 'including the day-name area. Inclusion of a ' 'graphic does not cover the day-name area.')) default_style.add_paragraph_style("WC-Month", para) # # WC-Text # font = BaseDoc.FontStyle() font.set(face=BaseDoc.FONT_SERIF, size=16, italic=1, color=((0x80, 0x0, 0x0))) para = BaseDoc.ParagraphStyle() para.set_font(font) para.set(bgcolor=((0xf0, 0xf8, 0xff))) para.set_alignment(BaseDoc.PARA_ALIGN_LEFT) para.set_description(_('The style used for text in the body of the ' 'calendar, it controls font size, face, style, ' 'color, and alignment. The background color is ' 'used ONLY for cells containing text, allowing ' 'for high-lighting of dates.')) default_style.add_paragraph_style("WC-Text", para) # # WC-Note # font = BaseDoc.FontStyle() font.set(face=BaseDoc.FONT_SANS_SERIF, size=16, color=((0x0, 0x0, 0x0))) para = BaseDoc.ParagraphStyle() para.set_font(font) para.set(bgcolor=((0xff, 0xff, 0xff))) para.set_alignment(BaseDoc.PARA_ALIGN_LEFT) para.set_description(_('The style used for notes at the bottom of the ' 'calendar, it controls font size, face, style, ' 'color and positioning. The background color ' 'setting affect all EMPTY calendar cells.')) default_style.add_paragraph_style("WC-Note", para) # # WC-Table # font = BaseDoc.FontStyle() font.set(face=BaseDoc.FONT_SERIF, size=24, color=((0x80, 0x0, 0x0))) para = BaseDoc.ParagraphStyle() para.set_font(font) para.set(bgcolor=((0xff, 0xff, 0xff))) para.set_alignment(BaseDoc.PARA_ALIGN_RIGHT) para.set_description(_('The style used for the table itself. This ' 'affects the color of the table lines and the ' 'color, font, size, and positioning of the ' 'calendar date numbers. It also controls the ' 'color of the day names.')) default_style.add_paragraph_style("WC-Table", para) #------------------------------------------------------------------------ # # # #------------------------------------------------------------------------ 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 + ("\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 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 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 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 = 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 # 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() #------------------------------------------------------------------------- # # # #------------------------------------------------------------------------- register_report( name = 'WebCal', category = CATEGORY_WEB, report_class = WebCalReport, options_class = WebCalOptions, modes = MODE_GUI, translated_name = _("Web Calendar..."), status = _("Beta"), author_name="Thom Sturgill", author_email="thsturgill@yahoo.com", description=_("Generates web (HTML) calendars."), )
\n') of.write(' %s
') of.write(GrampsLocale.short_days[1]) of.write('') of.write(GrampsLocale.short_days[day_col+2]) of.write('') of.write(GrampsLocale.short_days[7]) of.write('
  %s' % (cellclass,str(thisday.day))) something_this_week = 1 if list > []: of.write('
') for p in list: lines = p.count("\n") + 1 # lines in the text current = 0 for line in p.split("\n"): of.write(line) of.write('
') current += 1 of.write('
') of.write('
 ' % str(colspan)) if self.Note[month-1].strip() != '': of.write(self.Note[month-1]) else: of.write(" ") of.write('