diff --git a/ChangeLog b/ChangeLog index 063cf47d0..9fbf71b39 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-01-03 Douglas S. Blank + * src/ReportBase/_Constants.py: added QR_NOTE, QR_DATE + * src/gen/lib/date.py: working on date math + * src/plugins/AgeOnDate.py: new Quick View for age on date + * src/plugins/DefaultGadgets.py: added Age on Date gadget + * src/QuickReports.py: new constants + * src/Simple/_SimpleTable.py: handle sort columns + 2008-01-03 Gary Burton * src/Selectors/_SelectPerson.py: the select person dialog now allows a person to be selected by using the keyboard. This dialog really needs an diff --git a/src/QuickReports.py b/src/QuickReports.py index fa1d27cea..628eaff55 100644 --- a/src/QuickReports.py +++ b/src/QuickReports.py @@ -59,7 +59,8 @@ import gtk from PluginUtils import Plugins from ReportBase import CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY,\ CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE,\ - CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY + CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, \ + CATEGORY_QR_NOTE, CATEGORY_QR_DATE def create_quickreport_menu(category,dbstate,uistate,handle) : diff --git a/src/ReportBase/_Constants.py b/src/ReportBase/_Constants.py index 1651e26ad..b85e89314 100644 --- a/src/ReportBase/_Constants.py +++ b/src/ReportBase/_Constants.py @@ -71,3 +71,5 @@ CATEGORY_QR_EVENT = 2 CATEGORY_QR_SOURCE = 3 CATEGORY_QR_PLACE = 4 CATEGORY_QR_REPOSITORY = 5 +CATEGORY_QR_NOTE = 6 +CATEGORY_QR_DATE = 7 diff --git a/src/Simple/_SimpleTable.py b/src/Simple/_SimpleTable.py index 5d3c64021..063503d83 100644 --- a/src/Simple/_SimpleTable.py +++ b/src/Simple/_SimpleTable.py @@ -54,11 +54,13 @@ class SimpleTable: Set the columns """ self.__columns = list(copy.copy(columns)) + self.__sort_vals = [[] for i in range(len(self.__columns))] def on_table_doubleclick(self, obj, path, view_column): """ Handle events on tables. obj is a treeview """ + from QuickReports import run_quick_report_by_name from Editors import (EditPerson, EditEvent, EditFamily, EditSource, EditPlace, EditRepository) selection = obj.get_selection() @@ -140,11 +142,16 @@ class SimpleTable: return True return False # didn't handle event + def row_sort_val(self, col, val): + """ + Adds a row of data to sort by. + """ + self.__sort_vals[col].append(val) + def row(self, *data): """ Add a row of data. """ - from QuickReports import run_quick_report_by_name retval = [] link = None for item in data: @@ -256,19 +263,30 @@ class SimpleTable: treeview.connect('cursor-changed', self.on_table_click) renderer = gtk.CellRendererText() types = [int] # index + cnt = 0 + sort_data = [] + sort_data_types = [] for col in self.__columns: types.append(type(col)) column = gtk.TreeViewColumn(col,renderer,text=model_index) - column.set_sort_column_id(model_index) + if self.__sort_vals[cnt] != []: + sort_data.append(self.__sort_vals[cnt]) + column.set_sort_column_id(len(self.__columns) + + len(sort_data)) + sort_data_types.append(int) + else: + column.set_sort_column_id(model_index) treeview.append_column(column) #if model_index == sort_index: # FIXME: what to set here? model_index += 1 + cnt += 1 if self.title: self.simpledoc.paragraph(self.title) # Make a GUI to put the tree view in frame = gtk.Frame() frame.add(treeview) + types += sort_data_types model = gtk.ListStore(*types) treeview.set_model(model) iter = buffer.get_end_iter() @@ -276,7 +294,7 @@ class SimpleTable: text_view.add_child_at_anchor(frame, anchor) count = 0 for data in self.__rows: - model.append(row=([count] + list(data))) + model.append(row=([count] + list(data) + [col[count] for col in sort_data])) count += 1 frame.show_all() self.simpledoc.paragraph("") diff --git a/src/gen/lib/date.py b/src/gen/lib/date.py index b364763cb..9a7fee41c 100644 --- a/src/gen/lib/date.py +++ b/src/gen/lib/date.py @@ -259,18 +259,34 @@ class Date: days = d1[2] - d2[2] months = d1[1] - d2[1] years = d1[0] - d2[0] - if months < 0: + while days < 0: + months -= 1 + days = 30 + days + while months < 0: years -= 1 months = 12 + months - if days < 0: - months -= 1 - days = 31 + days + if days > 31: + months += days / 31 + days = days % 31 + if months > 11: + years += months / 12 + months = months % 12 + if years < 0: # can happen with 0 based months/days + years = 0 + months = 12 - months + days = 31 - days return (years, months, days) elif type(other) in [tuple, list]: return self.copy_offset_ymd(*map(lambda x: -x, other)) else: raise AttributeError, "unknown date sub type: %s " % type(other) + def __lt__(self, other): + return self.sortval < other.sortval + + def __gt__(self, other): + return self.sortval > other.sortval + def is_equal(self, other): """ Return 1 if the given Date instance is the same as the present diff --git a/src/plugins/AgeOnDate.py b/src/plugins/AgeOnDate.py new file mode 100644 index 000000000..b1d78e939 --- /dev/null +++ b/src/plugins/AgeOnDate.py @@ -0,0 +1,104 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2007 Donald N. Allingham +# 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 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 +# + +""" +Display references for any object +""" + +from Simple import SimpleAccess, SimpleDoc, SimpleTable +from gettext import gettext as _ +from PluginUtils import register_quick_report +from ReportBase import CATEGORY_QR_DATE +import DateHandler +import gen.lib +import Config + +def run(database, document, date): + """ + Display people probably alive and their ages on a particular date. + """ + # setup the simple access functions + sdb = SimpleAccess(database) + sdoc = SimpleDoc(document) + stab = SimpleTable(sdb, sdoc) + if not date.get_valid(): + sdoc.paragraph("Date is not a valid date.") + return + # display the title + sdoc.title(_("People probably alive and their ages on %s") % + DateHandler.displayer.display(date)) + sdoc.paragraph("\n") + stab.columns(_("Person"), _("Age")) # Actual Date makes column unicode + for person in sdb.all_people(): + birth_date = None + birth_str = "" + birth_sort = 0 + birth_ref = gen.lib.Person.get_birth_ref(person) + birth_date = get_event_date_from_ref(database, birth_ref) + death_ref = gen.lib.Person.get_death_ref(person) + death_date = get_event_date_from_ref(database, death_ref) + if birth_date: + if (birth_date.get_valid() and birth_date < date and + birth_date.get_year() != 0 and + ((death_date == None) or (death_date > date))): + diff_tuple = (date - birth_date) + if ((death_date != None) or + (death_date == None and + diff_tuple[0] <= Config.get(Config.MAX_AGE_PROB_ALIVE))): + if diff_tuple[1] != 0: + birth_str = ((_("%d years") % diff_tuple[0])+ ", " + + (_("%d months") % diff_tuple[1])) + else: + birth_str = (_("%d years") % diff_tuple[0]) + birth_sort = int(diff_tuple[0] * 12 + diff_tuple[1]) # months + if birth_str != "": + stab.row(person, birth_str) + stab.row_sort_val(1, birth_sort) + stab.write() + sdoc.paragraph("") + +def get_event_date_from_ref(database, ref): + date = None + if ref: + handle = ref.get_reference_handle() + if handle: + event = database.get_event_from_handle(handle) + if event: + date = event.get_date_object() + return date + + +#------------------------------------------------------------------------ +# +# Register the report +# +#------------------------------------------------------------------------ + +register_quick_report( + name = 'ageondate', + category = CATEGORY_QR_DATE, + run_func = run, + translated_name = _("Age on Date"), + status = _("Stable"), + description= _("Display people and ages on a particular date"), + author_name="Douglas Blank", + author_email="dblank@cs.brynmawr.edu" + ) diff --git a/src/plugins/DefaultGadgets.py b/src/plugins/DefaultGadgets.py index 26bc91f67..24631f617 100644 --- a/src/plugins/DefaultGadgets.py +++ b/src/plugins/DefaultGadgets.py @@ -551,6 +551,44 @@ class NewsGadget(Gadget): #print " after:", text yield False, text +class AgeOnDateGadget(Gadget): + def init(self): + import gtk + # GUI setup: + self.tooltip = _("Enter a date, click Run") + vbox = gtk.VBox() + hbox = gtk.HBox() + # label, entry + description = gtk.TextView() + description.set_wrap_mode(gtk.WRAP_WORD) + buffer = description.get_buffer() + buffer.set_text(_("Enter a date in the entry below and click Run." + " This will compute the ages for everyone in your" + " Family Tree on that date. You can then sort by" + " the age column, and double-click the row to view" + " or edit.")) + label = gtk.Label() + label.set_text(_("Date") + ":") + self.entry = gtk.Entry() + button = gtk.Button(_("Run")) + button.connect("clicked", self.run) + hbox.pack_start(label, False) + hbox.pack_start(self.entry, True) + vbox.pack_start(description, True) + vbox.pack_start(hbox, False) + vbox.pack_start(button, False) + self.gui.scrolledwindow.remove(self.gui.textview) + self.gui.scrolledwindow.add_with_viewport(vbox) + vbox.show_all() + + def run(self, obj): + text = self.entry.get_text() + date = DateHandler.parser.parse(text) + run_quick_report_by_name(self.gui.dbstate, + self.gui.uistate, + 'ageondate', + date) + register(type="gadget", name= "Top Surnames Gadget", @@ -621,3 +659,11 @@ register(type="gadget", title=_("News"), ) +register(type="gadget", + name="Age on Date Gadget", + tname=_("Age on Date Gadget"), + height=200, + content = AgeOnDateGadget, + title=_("Age on Date"), + ) +