Code Search for Developers
 
 
  

Calendar.py from gramps at Krugle


Show Calendar.py syntax highlighted

# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006  Donald N. Allingham
#
# 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

__author__ = "Douglas Blank <Doug.Blank@gmail.com>"
__version__ = "$Revision: 8254 $"

#------------------------------------------------------------------------
#
# python modules
#
#------------------------------------------------------------------------
from gettext import gettext as _
from xml.parsers import expat
import datetime
import time
import const
import os

#------------------------------------------------------------------------
#
# GRAMPS modules
#
#------------------------------------------------------------------------
import BaseDoc
from PluginUtils import register_report
from ReportBase import Report, ReportUtils, ReportOptions, \
     CATEGORY_DRAW, CATEGORY_TEXT, MODE_GUI, MODE_BKI, MODE_CLI
pt2cm = ReportUtils.pt2cm
cm2pt = ReportUtils.cm2pt
from Filters import GenericFilter, ParamFilter, Rules
import GrampsLocale
import RelLib
from Utils import probably_alive
from FontScale import string_trim, string_width

#------------------------------------------------------------------------
#
# The one and only GUI. This will be able to be moved to the 
# Widget once it is finished.
#
#------------------------------------------------------------------------
import gtk

#------------------------------------------------------------------------
#
# Support functions
#
#------------------------------------------------------------------------
def easter(year):
    """
    Computes the year/month/day of easter. Currently hardcoded in
    holidays.xml. 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)

#------------------------------------------------------------------------
#
# Calendar
#
#------------------------------------------------------------------------
class Calendar(Report):
    """
    Creates the Calendar object that produces the report.
    """
    def __getitem__(self, item):
        """ Used to get items from various places. Could be moved up to Report. """
        if item in self.doc.style_list:
            # font is the only element people refer to in writing reports
            # from the style_list:
            return self.doc.style_list[item].get_font() 
        elif item in self.options_class.options_dict:
            # otherwise it is a option:
            return self.options_class.options_dict[item]
        else:
            raise AttributeError, ("no widget named '%s'" % item)

    def define_graphics_styles(self):
        """ Set up the report. Could be moved up to Report. """
        for widget in self.options_class.widgets:
            if widget.__class__.__name__ == "StyleWidget":
                widget.define_graphics_style(self.doc)

    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()) == RelLib.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 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):
        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.pluginsDir,
                     os.path.join(const.home_dir,"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_report(self):
        """ The short method that runs through each month and creates a page. """
        # initialize the dict to fill:
        self.calendar = {}
        # 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:
        for month in range(1, 13):
            self.print_page(month)

    def print_page(self, month):
        """
        This method actually writes the calendar page.
        """
        self.doc.start_page()
        width = self.doc.get_usable_width()
        height = self.doc.get_usable_height()
        header = 2.54 # one inch
        self.draw_rectangle("CAL-Border", 0, 0, width, height)
        self.doc.draw_box("CAL-Title", "", 0, 0, width, header)
        self.doc.draw_line("CAL-Border", 0, header, width, header)
        year = self["year"]
        title = "%s %d" % (GrampsLocale.long_months[month], year)
        font_height = pt2cm(self["CAL-Title"].get_size())
        self.doc.center_text("CAL-Title", title, width/2, font_height * 0.25)
        cell_width = width / 7
        cell_height = (height - header)/ 6
        current_date = datetime.date(year, month, 1)
        spacing = pt2cm(1.25 * self["CAL-Text"].get_size()) # 158
        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()
        for day_col in range(7):
            font_height = pt2cm(self["CAL-Daynames"].get_size())
            self.doc.center_text("CAL-Daynames", 
                                 GrampsLocale.long_days[day_col+1],
                                 day_col * cell_width + cell_width/2,
                                 header - font_height * 1.5)
        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
                    self.draw_rectangle("CAL-Border", day_col * cell_width,
                                        header + week_row * cell_height,
                                        (day_col + 1) * cell_width,
                                        header + (week_row + 1) * cell_height)
                    last_edge = (day_col + 1) * cell_width
                    self.doc.center_text("CAL-Numbers", str(thisday.day),
                                         day_col * cell_width + cell_width/2,
                                         header + week_row * cell_height)
                    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"):
                            # make sure text will fit:
                            numpos = pt2cm(self["CAL-Numbers"].get_size())
                            if position + (current * spacing) - 0.1 >= cell_height - numpos: # font daynums
                                continue
                            font = self["CAL-Text"]
                            line = string_trim(font, line, cm2pt(cell_width + 0.2))
                            self.doc.draw_text("CAL-Text", line, 
                                              day_col * cell_width + 0.1,
                                              header + (week_row + 1) * cell_height - position + (current * spacing) - 0.1)
                            current += 1
                current_ord += 1
        if not something_this_week:
            last_edge = 0
        font_height = pt2cm(1.5 * self["CAL-Text1style"].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) 
        self.doc.end_page()

    def collect_data(self):
        """
        This method runs through the data, and collects the relevant dates
        and text.
        """
        filter_num = self.options_class.get_filter_number()
        filters = self.options_class.get_report_filters(self.start_person)
        self.filter = filters[filter_num]
        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()
            alive = probably_alive(person, self.database, self["year"])
            if self["birthdays"] and birth_date != None and ((self["alive"] and alive) 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["maiden_name"] == 0: # get husband's last name:
                    if person.get_gender() == RelLib.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["anniversaries"] and ((self["alive"] and alive) 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, self["year"]):
                                    continue
                            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)

class CalendarReport(Calendar):
    def write_report(self):
        """ The short method that runs through each month and creates a page. """
        # initialize the dict to fill:
        self.calendar = {}
        # get the information, first from holidays:
        if self["country"] != 0:
            self.get_holidays(self["year"], _countries[self["country"]]) # currently global
        # 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()
        for month in range(1, 13):
            self.print_page(month)
    def print_page(self, month):
        year = self["year"]
        self.doc.start_paragraph('BIR-Monthstyle')
        self.doc.write_text("%s %d" % (GrampsLocale.long_months[month], year))
        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("%s %s" % (GrampsLocale.long_months[month], 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

###################################################################################
# These classes are a suggested of how to rework the graphics out of reports. It also
# makes these items abstractions, which makes it easy to change the report
# infrastructure without having everyone rewrite their reports each time.
#
# This builds on the current document code, so no changes are needed.
###################################################################################
class Widget:
    """ A Widget virtual base class. This contains no graphics specifics. """
    commonDefaults = {
        "wtype"      : None,
        "name"       : None,
        "label"      : None,
        "help"       : None,
        "wtype"      : None,
        "valid_text" : None,
        "frame"      : None,
        "value"      : None,
        }
    defaults = {}
    def __init__(self, option_object, **args):
        self.option_object = option_object
        self.setup(args)
        self.register()
    def __getitem__(self, key):
        if key in self.settings:
            return self.settings[key]
        else:
            raise AttributeError, ("no widget attribute named '%s'" % key)
    def __setitem__(self, key, value):
        self.settings[key] = value
    def setup(self, args = {}):
        # start with the base defaults common to all:
        self.settings = self.commonDefaults.copy()
        # now add those from the subclass:
        self.settings.update(self.defaults)
        # ad finally, those from the user:
        self.settings.update(args)
    def register(self):
        className = self.__class__.__name__
        if className == "FilterWidget":
            self.option_object.enable_dict['filter'] =  0
        elif className == "StyleWidget":
            self.option_object[self["name"]] = self["value"]
        else:
            self.option_object[self["name"]] = self["value"]
            self.option_object.options_help[self["name"]] = (
                self["wtype"], self["help"], self["valid_text"])
    def add_gui(self, dialog): pass
    def update(self): pass
class SpinWidget(Widget):
    """ A spinner number selector widget for GTK. """
    defaults = {
        "wtype"     : "=num",
        "help"      : "Numeric option",
        "valid_text": "Any number",
        }
    def add_gui(self, dialog):
        keyword = self["name"]
        obj = self.option_object.__dict__
        obj[keyword] = gtk.SpinButton()
        obj[keyword].set_digits(0)
        obj[keyword].set_increments(1,2)
        obj[keyword].set_range(0,2100)
        obj[keyword].set_numeric(True)
        obj[keyword].set_value(self.option_object[keyword])
        if self["frame"] != None:
            dialog.add_frame_option(self["frame"], self["label"], obj[keyword])
        else:
            dialog.add_option(self["label"], obj[keyword])
    def update(self):
        dict = self.option_object.__dict__
        keyword = self["name"]
        self.option_object[keyword] = dict[keyword].get_value_as_int()
        self[keyword] = dict[keyword].get_value_as_int()
class SelectionWidget(Widget):
    """ A selection widget for GTK. """
    defaults = {
        "wtype"     : "=0/1",
        "help"      : "Selection option",
        "valid_text": "Any choice",
        }
    def add_gui(self, dialog):
        keyword = self["name"]
        obj = self.option_object.__dict__
        obj[keyword] = gtk.ComboBox()
        store = gtk.ListStore(str)
        obj[keyword].set_model(store)
        cell = gtk.CellRendererText()
        obj[keyword].pack_start(cell,True)
        obj[keyword].add_attribute(cell,'text',0)
        for item in self["options"]:
            store.append(row=[item[2]])
        obj[keyword].set_active(self.option_object[keyword])
        if self["frame"] != None:
            dialog.add_frame_option(self["frame"], self["label"], obj[keyword]) # 4th is help
        else:
            dialog.add_option(self["label"], obj[keyword])
    def update(self):
        dict = self.option_object.__dict__
        keyword = self["name"]
        self.option_object[keyword] = dict[keyword].get_active()
        self[keyword] = dict[keyword].get_active()
class CheckWidget(Widget):
    """ A check box widget for GTK. """
    defaults = {
        "wtype"     : "=0/1",
        "help"      : "Yes/No option",
        "valid_text": "1 for yes, 0 for no",
        }
    def add_gui(self, dialog):
        keyword = self["name"]
        obj = self.option_object.__dict__        
        obj[keyword] = gtk.CheckButton(self["label"])
        obj[keyword].set_active(self.option_object[keyword])
        if self["frame"] != None:
            dialog.add_frame_option(self["frame"], "", obj[keyword])
        else:
            dialog.add_option("", obj[keyword])
    def update(self):
        dict = self.option_object.__dict__
        keyword = self["name"]
        self.option_object[keyword] = int(dict[keyword].get_active())
        self[keyword] = int(dict[keyword].get_active())
class EntryWidget(Widget):
    """ A text widget for GTK. """
    defaults = {
        "wtype"     : "=str",
        "help"      : "String option",
        "valid_text": "Any textual data",
        }
    def add_gui(self, dialog):
        keyword = self["name"]
        obj = self.option_object.__dict__
        obj[keyword] = gtk.Entry()
        obj[keyword].set_text(self.option_object[keyword])
        if self["frame"] != None:
            dialog.add_frame_option(self["frame"], self["label"], obj[keyword])
        else:
            dialog.add_option(self["label"], obj[keyword])            
    def update(self):
        dict = self.option_object.__dict__
        keyword = self["name"]
        self.option_object[keyword] = unicode(dict[keyword].get_text())
        self[keyword] = unicode(dict[keyword].get_text())
class NumberWidget(EntryWidget):
    """ A number widget for GTK. """
    defaults = {
        "wtype"     : "=num",
        "help"      : "Numeric option",
        "valid_text": "Any number",
        }
    def add_gui(self, dialog):
        keyword = self["name"]
        obj = self.option_object.__dict__
        obj[keyword] = gtk.Entry()
        obj[keyword].set_text(str(self.option_object[keyword]))
        if self["frame"] != None:
            dialog.add_frame_option(self["frame"], self["label"], obj[keyword])
        else:
            dialog.add_option(self["label"], obj[keyword])            
    def update(self):
        dict = self.option_object.__dict__
        keyword = self["name"]
        text = dict[keyword].get_text()
        # Is there a way to check that this won't fail?
        try:
            value = float(text)
        except:
            value = 0.0
        self.option_object[keyword] = value
        self[keyword] = value
class StyleWidget(Widget):
    defaults = {
        "size"      : 8,
        "bold"      : 0,
        "italics"   : 0,
        "type_face" : BaseDoc.FONT_SERIF,
        "fill_color": (0xFF,0xFF, 0xFF),
        "borders"   : False,
        "justified" : "left",
        "indent"    : 0.0,
        }
    def make_default_style(self, default_style):
        f = BaseDoc.FontStyle()
        f.set_size(self["size"])
        f.set_italic(self["italics"])
        f.set_bold(self["bold"])
        f.set_type_face(self["type_face"])
        p = BaseDoc.ParagraphStyle()
        p.set_font(f)
        p.set_description(self["label"])
        p.set(first_indent=self["indent"])
        if self["justified"] == "left":
            p.set_alignment(BaseDoc.PARA_ALIGN_LEFT)       
        elif self["justified"] == "right":
            p.set_alignment(BaseDoc.PARA_ALIGN_RIGHT)       
        elif self["justified"] == "center":
            p.set_alignment(BaseDoc.PARA_ALIGN_CENTER)       
        if self["borders"]:
            p.set_top_border(True)
            p.set_left_border(True)
            p.set_bottom_border(True)
            p.set_right_border(True)
        else:
            p.set_top_border(False)
            p.set_left_border(False)
            p.set_bottom_border(False)
            p.set_right_border(False)
        default_style.add_style(self["name"], p)
    def define_graphics_style(self, document):
        g = BaseDoc.GraphicsStyle()
        g.set_paragraph_style(self["name"])
        g.set_fill_color(self["fill_color"])
        if self["borders"]:
            g.set_line_width(1) 
        else:
            g.set_line_width(0) 
        # FIXME: add all other graphics items (color, etc) here
        document.add_draw_style(self["name"], g)
class FilterWidget(Widget):
    """
    A filter widget. This doesn't have the GTK code here, but should.
    This class takes names of the filters and does everything for you.
    "all filters" - all of them
    "everyone" - all people in table
    "descendents" - direct descendents
    "descendent familes" - direct descendents and their familes
    "ancestors" - all ancestors of person
    "common ancestors" - all common ancestors
    "calendar attribute" - experimental filter for tagging people
    """
    def get_filters(self, person):
        """Set up the list of possible content filters."""
        if person:
            name = person.get_primary_name().get_name()
            gramps_id = person.get_gramps_id()
        else:
            name = 'PERSON'
            gramps_id = ''
        retval = []
        for filter in self["filters"]:
            if filter in ["everyone", "all filters"]:
                f = GenericFilter()
                f.set_name(_("Entire Database"))
                f.add_rule(Rules.Person.Everyone([]))
                retval.append(f)
            if filter in ["descendants", "all filters"]:
                f = GenericFilter()
                f.set_name(_("Descendants of %s") % name)
                f.add_rule(Rules.Person.IsDescendantOf([gramps_id,1]))
                retval.append(f)
            if filter in ["descendant families", "all filters"]:
                f = GenericFilter()
                f.set_name(_("Descendant Families of %s") % name)
                f.add_rule(Rules.Person.IsDescendantFamilyOf([gramps_id,1]))
                retval.append(f)
            if filter in ["ancestors", "all filters"]:
                f = GenericFilter()
                f.set_name(_("Ancestors of %s") % name)
                f.add_rule(Rules.Person.IsAncestorOf([gramps_id,1]))
                retval.append(f)
            if filter in ["common ancestors", "all filters"]:
                f = GenericFilter()
                f.set_name(_("People with common ancestor with %s") % name)
                f.add_rule(Rules.Person.HasCommonAncestorWith([gramps_id]))
                retval.append(f)
            if filter in ["calendar attribute", "all filters"]:
                f = ParamFilter()
                f.set_name(_("People with a Calendar attribute"))
                f.add_rule(Rules.Person.HasTextMatchingSubstringOf(['Calendar',0,0]))
                retval.append(f)

        from Filters import CustomFilters
        retval.extend(CustomFilters.get_filters('Person'))
        return retval

# -----------------------------------------------------------------
# The following could all be moved to the parent class, if you wanted
# to adopt this report reworking. Even if you didn't want to use them
# it would be ok to put there, because self.widgets would be empty.
# -----------------------------------------------------------------

class NewReportOptions(ReportOptions):
    """
    Defines options and provides code to handling the interface.
    This is free of any graphics specifics.
    """
    def __getitem__(self, keyword):
        """ This could be moved up to ReportOptions """
        if keyword in self.options_dict:
            return self.options_dict[keyword]
        else:
            raise AttributeError, ("no widget named '%s'" % keyword)

    def __setitem__(self, keyword, value):
        """ This could be moved up to ReportOptions """
        self.options_dict[keyword] = value

    def add_user_options(self,dialog):
        for widget in self.widgets:
            widget.add_gui(dialog)

    def parse_user_options(self,dialog):
        for widget in self.widgets:
            widget.update()
        
    def get_report_filters(self,person):
        for widget in self.widgets:
            if widget.__class__.__name__ == "FilterWidget":
                return widget.get_filters(person)

    def make_default_style(self,default_style):
        for widget in self.widgets:
            if widget.__class__.__name__ == "StyleWidget":
                widget.make_default_style(default_style)

class CalendarOptions(NewReportOptions):
    def enable_options(self):
        self.enable_dict = {}
        self.widgets = [
            FilterWidget(self, label = _("Filter"),
                         name = "filter",
                         filters = ["all filters"]),
            EntryWidget(self, label = _("Text 1"),
                        name  = "text1",
                        value = "My Calendar",
                        help  = "Large text area",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            EntryWidget(self, label = _("Text 2"),
                        name  = "text2",
                        value = "Produced with GRAMPS",
                        help  = "Medium size text",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            EntryWidget(self, label = _("Text 3"),
                        name  = "text3",
                        value = "http://gramps-project.org/",
                        help  = "Small text area",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            SpinWidget(self, label = _("Year of calendar"),
                       name  = "year",
                       value = time.localtime()[0], # the current year
                       help  = "Year of calendar",
                       valid_text = "Any year",
                       ),
            SelectionWidget(self, label = _("Country for holidays"),
                        name = "country",
                        value = 0, # Don't include holidays
                        options = map(lambda c: ("", c, c), _countries),
                        help = "Select the country to see associated holidays.",
                        valid_text = "Select a country to see those holidays.",
                        ),
            SelectionWidget(self, label = _("Birthday surname"),
                        name = "maiden_name",
                        value = 1,
                        options = [
                                   ("regular",
                                    "Wives use husband's surname",
                                    _("Wives use husband's surname")),
                                   ("maiden",
                                    "Wives use their own surname",
                                    _("Wives use their own surname")),
                                   ],
                        help = "Select married women's maiden name.",
                        valid_text = "Select to use married women's maiden name.",
                        ),
            CheckWidget(self, label = _("Only include living people"),
                        name = "alive",
                        value = 1,
                        help = "Include only living people",
                        valid_text = "Select to only include living people",
                        ),
            CheckWidget(self, label = _("Include birthdays"),
                        name = "birthdays",
                        value = 1,
                        help = "Include birthdays",
                        valid_text = "Select to include birthdays",
                        ),
            CheckWidget(self, label = _("Include anniversaries"),
                        name = "anniversaries",
                        value = 1,
                        help = "Include anniversaries",
                        valid_text = "Select to include anniversaries",
                        ),
            StyleWidget(self, label = _('Title text and background color.'),
                        name = "CAL-Title",
                        size = 20,
                        italics = 1,
                        bold = 1,
                        fill_color = (0xEA,0xEA,0xEA),
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Border lines of calendar boxes.'),
                        name = "CAL-Border",
                        borders = True,
                        ),
            StyleWidget(self, label = _('Calendar day numbers.'),
                        name = "CAL-Numbers",
                        size = 13,
                        bold = 1,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Daily text display.'),
                        name = "CAL-Text",
                        size = 9,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Days of the week text.'),
                        name = "CAL-Daynames",
                        size = 12,
                        italics = 1,
                        bold = 1,
                        fill_color = (0xEA,0xEA,0xEA),
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Text at bottom, line 1.'),
                        name = "CAL-Text1style",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Text at bottom, line 2.'),
                        name = "CAL-Text2style",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Text at bottom, line 3.'),
                        name = "CAL-Text3style",
                        size = 9,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            ]


class CalendarReportOptions(NewReportOptions):
    def enable_options(self):
        self.enable_dict = {}
        self.widgets = [
            FilterWidget(self, label = _("Filter"),
                         name = "filter",
                         filters = ["all filters"]),
            EntryWidget(self, label = _("Title text"),
                        name  = "titletext",
                        value = "Birthday and Anniversary Report",
                        help  = "Title of report",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            EntryWidget(self, label = _("Text 1"),
                        name  = "text1",
                        value = "Created with GRAMPS",
                        help  = "Extra text area, line 1",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            EntryWidget(self, label = _("Text 2"),
                        name  = "text2",
                        value = "Open source genealogy program",
                        help  = "Extra text area, line 2",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            EntryWidget(self, label = _("Text 3"),
                        name  = "text3",
                        value = "http://gramps-project.org/",
                        help  = "Extra text area, line 3",
                        valid_text = "Any text",
                        frame = _("Text Options")
                        ),                       
            SpinWidget(self, label = _("Year of report"),
                       name  = "year",
                       value = time.localtime()[0], # the current year
                       help  = "Year of report",
                       valid_text = "Any year",
                       ),
            SelectionWidget(self, label = _("Country for holidays"),
                        name = "country",
                        value = 0, # Don't include holidays
                        options = map(lambda c: ("", c, c), _countries),
                        help = "Select the country to see associated holidays.",
                        valid_text = "Select a country to see those holidays.",
                        ),
            SelectionWidget(self, label = _("Birthday surname"),
                        name = "maiden_name",
                        value = 1,
                        options = [
                                   ("regular",
                                    "Wives use husband's surname",
                                    _("Wives use husband's surname")),
                                   ("maiden",
                                    "Wives use their own surname",
                                    _("Wives use their own surname")),
                                   ],
                        help = "Select married women's maiden name.",
                        valid_text = "Select to use married women's maiden name.",
                        ),
            CheckWidget(self, label = _("Only include living people"),
                        name = "alive",
                        value = 1,
                        help = "Include only living people",
                        valid_text = "Select to only include living people",
                        ),
            CheckWidget(self, label = _("Include birthdays"),
                        name = "birthdays",
                        value = 1,
                        help = "Include birthdays",
                        valid_text = "Select to include birthdays",
                        ),
            CheckWidget(self, label = _("Include anniversaries"),
                        name = "anniversaries",
                        value = 1,
                        help = "Include anniversaries",
                        valid_text = "Select to include anniversaries",
                        ),
            StyleWidget(self, label = _('Title text style'),
                        name = "BIR-Title",
                        size = 14,
                        bold = 1,
                        type_face = BaseDoc.FONT_SERIF,
                        justified = "center",
                        ),
            StyleWidget(self, label = _('Data text style'),
                        name = "BIR-Datastyle",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        indent = 1.0,
                        ),
            StyleWidget(self, label = _('Month text style'),
                        name = "BIR-Monthstyle",
                        size = 12,
                        bold = 1,
                        type_face = BaseDoc.FONT_SERIF,
                        ),
            StyleWidget(self, label = _('Day text style'),
                        name = "BIR-Daystyle",
                        size = 12,
                        bold = 1,
                        italics = 1,
                        type_face = BaseDoc.FONT_SERIF,
                        indent = .5,
                        ),
            StyleWidget(self, label = _('Extra text style, line 1.'),
                        name = "BIR-Text1style",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        justified = "center",
                        ),
            StyleWidget(self, label = _('Extra text style, line 2.'),
                        name = "BIR-Text2style",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        justified = "center",
                        ),
            StyleWidget(self, label = _('Extra text style, line 3.'),
                        name = "BIR-Text3style",
                        size = 12,
                        type_face = BaseDoc.FONT_SERIF,
                        justified = "center",
                        ),
            ]

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 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 get_countries():
    """ Looks in multiple places for holidays.xml files """
    locations = [const.pluginsDir,
                 os.path.join(const.home_dir,"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

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()

#------------------------------------------------------------------------
#
# Register the plugins
#
#------------------------------------------------------------------------
register_report(
    name = 'calendar',
    category = CATEGORY_DRAW,
    report_class = Calendar,
    options_class = CalendarOptions,
    modes = MODE_GUI | MODE_BKI | MODE_CLI,
    translated_name = _("Calendar"),
    status = _("Stable"),
    author_name = "Douglas S. Blank",
    author_email = "dblank@cs.brynmawr.edu",
    description = _("Produces a graphical calendar"),
    )
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"),
    )




See more files for this project here

gramps

GRAMPS is a GNOME genealogy program for Linux and FreeBSD that allows you to easily build\r\nand keep track of your family tree.

Project homepage: http://sourceforge.net/projects/gramps
Programming language(s): Python
License: other

  AncestorChart.py
  AncestorReport.py
  Ancestors.py
  BookReport.py
  Calendar.py
  ChangeNames.py
  ChangeTypes.py
  Check.py
  Checkpoint.py
  CmdRef.py
  CountAncestors.py
  CustomBookText.py
  DesGraph.py
  Desbrowser.py
  DescendChart.py
  DescendReport.py
  DetAncestralReport.py
  DetDescendantReport.py
  DumpGenderStats.py
  EndOfLineReport.py
  Eval.py
  EventCmp.py
  EventNames.py
  ExportCSV.py
  ExportVCalendar.py
  ExportVCard.py
  ExtractCity.py
  FamilyGroup.py
  FamilyLines.py
  FanChart.py
  FindDupes.py
  FtmStyleAncestors.py
  FtmStyleDescendants.py
  GraphViz.py
  ImportCSV.py
  ImportGeneWeb.py
  ImportvCard.py
  IndivComplete.py
  IndivSummary.py
  KinshipReport.py
  Leak.py
  Makefile.am
  MarkerReport.py
  MediaManager.py
  NarrativeWeb.py
  OwnerEditor.py
  PHPGedViewConnector.py
  PatchNames.py
  ReadGrdb.py
  ReadPkg.py
  Rebuild.py
  RebuildRefMap.py
  RelCalc.py
  RemoveUnused.py
  ReorderIds.py
  SimpleBookTitle.py
  SoundGen.py
  StatisticsChart.py
  Summary.py
  TestcaseGenerator.py
  TimeLine.py
  Verify.py
  WebCal.py
  WriteCD.py
  WriteFtree.py
  WriteGeneWeb.py
  WritePkg.py
  all_events.py
  book.glade
  cdexport.glade
  changenames.glade
  changetype.glade
  checkpoint.glade
  csvexport.glade
  desbrowse.glade
  eval.glade
  eventcmp.glade
  genewebexport.glade
  holidays.xml
  leak.glade
  merge.glade
  ownereditor.glade
  patchnames.glade
  phpgedview.glade
  rel_cs.py
  rel_da.py
  rel_de.py
  rel_es.py
  rel_fi.py
  rel_fr.py
  rel_hu.py
  rel_it.py
  rel_nl.py
  rel_no.py
  rel_pl.py
  rel_ru.py
  rel_sk.py
  rel_sv.py
  relcalc.glade
  siblings.py
  soundex.glade
  summary.glade
  unused.glade
  vcalendarexport.glade
  vcardexport.glade
  verify.glade
  writeftree.glade