From 3ed982112ee88d019c4fa798f0f537fbcb9962f4 Mon Sep 17 00:00:00 2001 From: Gary Burton Date: Mon, 1 Sep 2008 20:08:39 +0000 Subject: [PATCH] Add a new place report svn: r11008 --- src/PluginUtils/_GuiOptions.py | 116 +++++++++ src/PluginUtils/_MenuOptions.py | 23 ++ src/PluginUtils/__init__.py | 3 +- src/ReportBase/_ReportOptions.py | 2 + src/plugins/PlaceReport.py | 400 +++++++++++++++++++++++++++++++ 5 files changed, 543 insertions(+), 1 deletion(-) create mode 100644 src/plugins/PlaceReport.py diff --git a/src/PluginUtils/_GuiOptions.py b/src/PluginUtils/_GuiOptions.py index 822e0ad4d..2a4aed0fc 100644 --- a/src/PluginUtils/_GuiOptions.py +++ b/src/PluginUtils/_GuiOptions.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2008 Brian G. Matherly +# Copyright (C) 2008 Gary Burton # # 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 @@ -900,6 +901,117 @@ class GuiPersonListOption(gtk.HBox): self.__model.remove(i) self.__update_value() +#------------------------------------------------------------------------- +# +# GuiPlaceListOption class +# +#------------------------------------------------------------------------- +class GuiPlaceListOption(gtk.HBox): + """ + This class displays a widget that allows multiple places from the + database to be selected. + """ + def __init__(self, option, dbstate, uistate, track, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.PlaceListOption + @return: nothing + """ + gtk.HBox.__init__(self) + self.__option = option + self.__dbstate = dbstate + self.__db = dbstate.get_database() + self.__uistate = uistate + self.__track = track + + self.__model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) + self.__tree_view = gtk.TreeView(self.__model) + self.__tree_view.set_size_request(150, 150) + col1 = gtk.TreeViewColumn(_('Place' ), gtk.CellRendererText(), text=0) + col2 = gtk.TreeViewColumn(_('ID' ), gtk.CellRendererText(), text=1) + col1.set_resizable(True) + col2.set_resizable(True) + col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) + col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) + col1.set_sort_column_id(0) + col2.set_sort_column_id(1) + self.__tree_view.append_column(col1) + self.__tree_view.append_column(col2) + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.add(self.__tree_view) + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.set_shadow_type(gtk.SHADOW_OUT) + + self.pack_start(self.__scrolled_window, expand=True, fill=True) + + value = self.__option.get_value() + for gid in value.split(): + place = self.__db.get_place_from_gramps_id(gid) + if place: + place_name = place.get_title() + self.__model.append([place_name, gid]) + + # now setup the '+' and '-' pushbutton for adding/removing places from + # the container + self.__add_place = widgets.SimpleButton(gtk.STOCK_ADD, + self.__add_place_clicked) + self.__del_place = widgets.SimpleButton(gtk.STOCK_REMOVE, + self.__del_place_clicked) + self.__vbbox = gtk.VButtonBox() + self.__vbbox.add(self.__add_place) + self.__vbbox.add(self.__del_place) + self.__vbbox.set_layout(gtk.BUTTONBOX_SPREAD) + self.pack_end(self.__vbbox, expand=False) + + tooltip.set_tip(self.__tree_view, self.__option.get_help()) + + def __update_value(self): + """ + Parse the object and return. + """ + gidlist = '' + i = self.__model.get_iter_first() + while (i): + gid = self.__model.get_value(i, 1) + gidlist = gidlist + gid + ' ' + i = self.__model.iter_next(i) + self.__option.set_value(gidlist) + + def __add_place_clicked(self, obj): # IGNORE:W0613 - obj is unused + """ + Handle the add place button. + """ + # places we already have must be excluded + # so we don't list them multiple times + skip_list = set() + i = self.__model.get_iter_first() + while (i): + gid = self.__model.get_value(i, 1) # get the GID stored in column #1 + place = self.__db.get_place_from_gramps_id(gid) + skip_list.add(place.get_handle()) + i = self.__model.iter_next(i) + + select_class = selector_factory('Place') + sel = select_class(self.__dbstate, self.__uistate, + self.__track, skip=skip_list) + place = sel.run() + if place: + place_name = place.get_title() + gid = place.get_gramps_id() + self.__model.append([place_name, gid]) + self.__update_value() + + def __del_place_clicked(self, obj): # IGNORE:W0613 - obj is unused + """ + Handle the delete place button. + """ + (path, column) = self.__tree_view.get_cursor() + if (path): + i = self.__model.get_iter(path) + self.__model.remove(i) + self.__update_value() + #------------------------------------------------------------------------- # # GuiSurnameColourOption class @@ -1332,6 +1444,10 @@ def make_gui_option(option, tooltips, dbstate, uistate, track): widget = GuiSurnameColourOption(option, dbstate, uistate, track, tooltips) + elif isinstance(option, _MenuOptions.PlaceListOption): + widget = GuiPlaceListOption(option, dbstate, + uistate, track, + tooltips) else: raise AttributeError("can't make GuiOption: unknown option type: '%s'" % option) return widget, label diff --git a/src/PluginUtils/_MenuOptions.py b/src/PluginUtils/_MenuOptions.py index fdd709e0c..0a3bfc374 100644 --- a/src/PluginUtils/_MenuOptions.py +++ b/src/PluginUtils/_MenuOptions.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2008 Brian G. Matherly +# Copyright (C) 2008 Gary Burton # # 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 @@ -518,6 +519,28 @@ class PersonListOption(Option): """ Option.__init__(self, label, "") +#------------------------------------------------------------------------- +# +# PlaceListOption class +# +#------------------------------------------------------------------------- +class PlaceListOption(Option): + """ + This class describes a widget that allows multiple places from the + database to be selected. + """ + def __init__(self, label): + """ + @param label: A label to be applied to this option. + Example: "Places" + @type label: string + @param value: A set of GIDs as initial values for this option. + Example: "111 222 333 444" + @type value: string + @return: nothing + """ + Option.__init__(self, label, "") + #------------------------------------------------------------------------- # # SurnameColourOption class diff --git a/src/PluginUtils/__init__.py b/src/PluginUtils/__init__.py index 17718675d..507455c2e 100644 --- a/src/PluginUtils/__init__.py +++ b/src/PluginUtils/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2001-2006 Donald N. Allingham # Copyright (C) 2008 Brian Matherly +# Copyright (C) 2008 Gary Burton # # 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 @@ -24,7 +25,7 @@ from _MenuOptions import (NumberOption, BooleanOption, TextOption, EnumeratedListOption, FilterOption, StringOption, ColourOption, PersonOption, PersonListOption, SurnameColourOption, FamilyOption, DestinationOption, - NoteOption, MediaOption, StyleOption) + NoteOption, MediaOption, StyleOption, PlaceListOption) from _GuiOptions import GuiMenuOptions, make_gui_option from _Options import Options, OptionListCollection, OptionList, OptionHandler diff --git a/src/ReportBase/_ReportOptions.py b/src/ReportBase/_ReportOptions.py index a949e27a8..39ea24785 100644 --- a/src/ReportBase/_ReportOptions.py +++ b/src/ReportBase/_ReportOptions.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2004-2007 Donald N. Allingham +# Copyright (C) 2008 Gary Burton # # 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 @@ -747,6 +748,7 @@ class ReportOptions(_Options.Options): FTD- FTM Style Descendant report IDS- Individual Complete Report IVS- Individual Summary Report + PLC- Place Report SBT- Simple Boot Title TLG- Timeline Graph """ diff --git a/src/plugins/PlaceReport.py b/src/plugins/PlaceReport.py new file mode 100644 index 000000000..f553586f2 --- /dev/null +++ b/src/plugins/PlaceReport.py @@ -0,0 +1,400 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Gary Burton +# +# 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 +# + +# $Id$ + +"""Place Report""" + +#------------------------------------------------------------------------ +# +# python modules +# +#------------------------------------------------------------------------ +from gettext import gettext as _ + +#------------------------------------------------------------------------ +# +# gramps modules +# +#------------------------------------------------------------------------ +from PluginUtils import PluginManager, PlaceListOption, FilterOption +from Filters import CustomFilters, GenericFilter +from ReportBase import Report, MenuReportOptions, \ + CATEGORY_TEXT, MODE_GUI, MODE_BKI, MODE_CLI +import BaseDoc +import DateHandler +import Sort +from BasicUtils import name_displayer as _nd + +class PlaceReport(Report): + """ + Place Report class + """ + def __init__(self, database, options_class): + """ + Create the PlaceReport object produces the Place report. + + The arguments are: + + database - the GRAMPS database instance + options_class - instance of the Options class for this report + + This report needs the following parameters (class variables) + that come in the options class. + + places - List of places to report on. + + """ + + Report.__init__(self, database, options_class) + + menu = options_class.menu + places = menu.get_option_by_name('places').get_value() + + filter_option = menu.get_option_by_name('filter') + self.filter = filter_option.get_filter() + self.sort = Sort.Sort(database) + + if self.filter.get_name() != '': + # Use the selected filter to provide a list of place handles + plist = self.database.get_place_handles(sort_handles=False) + self.place_handles = self.filter.apply(self.database, plist) + else: + # Use the place handles selected without a filter + self.place_handles = self.__get_place_handles(places) + + def write_report(self): + """ + The routine the actually creates the report. At this point, the document + is opened and ready for writing. + """ + + # Write the title line. Set in INDEX marker so that this section will be + # identified as a major category if this is included in a Book report. + + title = _("Place Report") + mark = BaseDoc.IndexMark(title, BaseDoc.INDEX_TYPE_TOC, 1) + self.doc.start_paragraph("PLC-ReportTitle") + self.doc.write_text(title, mark) + self.doc.end_paragraph() + self.__write_all_places() + + def __write_all_places(self): + """ + This procedure writes out each of the selected places. + """ + place_nbr = 1 + for handle in self.place_handles: + self.__write_place(handle, place_nbr) + self.__write_referenced_events(handle) + self.__write_referenced_persons(handle) + place_nbr += 1 + + def __write_place(self, handle, place_nbr): + """ + This procedure writes out the details of a single place + """ + place = self.database.get_place_from_handle(handle) + location = place.get_main_location() + + place_details = [_("Gramps ID: %s ") % place.get_gramps_id(), + _("Street: %s ") % location.get_street(), + _("Parish: %s ") % location.get_parish(), + _("City: %s ") % location.get_city(), + _("County: %s ") % location.get_county(), + _("State: %s") % location.get_state(), + _("Country: %s ") % location.get_country()] + self.doc.start_paragraph("PLC-PlaceTitle") + self.doc.write_text(("%(nbr)s. %(place)s") % + {'nbr' : place_nbr, + 'place' : place.get_title()}) + self.doc.end_paragraph() + + for item in place_details: + self.doc.start_paragraph("PLC-PlaceDetails") + self.doc.write_text(item) + self.doc.end_paragraph() + + def __write_referenced_events(self, handle): + """ + This procedure writes out each of the events related to the place + """ + event_handles = [event_handle for (object_type, event_handle) in \ + self.database.find_backlink_handles(handle)] + event_handles.sort(self.sort.by_date) + + if event_handles: + self.doc.start_paragraph("PLC-Section") + title = _("Events that happened at this place") + self.doc.write_text(title) + self.doc.end_paragraph() + self.doc.start_table("EventTable", "PLC-EventTable") + column_titles = [_("Date"), _("Type of Event"), _("Description")] + self.doc.start_row() + for title in column_titles: + self.doc.start_cell("PLC-TableColumn") + self.doc.start_paragraph("PLC-ColumnTitle") + self.doc.write_text(title) + self.doc.end_paragraph() + self.doc.end_cell() + self.doc.end_row() + + for handle in event_handles: + event = self.database.get_event_from_handle(handle) + if event: + date = DateHandler.get_date(event) + descr = event.get_description() + event_type = str(event.get_type()) + event_details = [date, event_type, descr] + self.doc.start_row() + for detail in event_details: + self.doc.start_cell("PLC-Cell") + self.doc.start_paragraph("PLC-Details") + self.doc.write_text("%s " % detail) + self.doc.end_paragraph() + self.doc.end_cell() + self.doc.end_row() + + if event_handles: + self.doc.end_table() + + def __write_referenced_persons(self, handle): + """ + This procedure writes out each of the people related to the place + """ + event_handles = [event_handle for (object_type, event_handle) in \ + self.database.find_backlink_handles(handle)] + + person_list = [] + for evt_handle in event_handles: + ref_handles = [x \ + for x in self.database.find_backlink_handles(evt_handle)] + for (ref_type, ref_handle) in ref_handles: + if ref_type == 'Person': + person_list.append(ref_handle) + else: + family = self.database.get_family_from_handle(ref_handle) + father = family.get_father_handle() + if father: + person_list.append(father) + mother = family.get_mother_handle() + if mother: + person_list.append(mother) + + # Weed out the duplicates where a person has multiple events at the + # place + person_list = list(set(person_list)) + person_list.sort(self.sort.by_last_name) + + if person_list: + self.doc.start_paragraph("PLC-Section") + title = _("People associated with this place") + self.doc.write_text(title) + self.doc.end_paragraph() + + for handle in person_list: + person = self.database.get_person_from_handle(handle) + if person: + self.doc.start_paragraph("PLC-Details") + self.doc.write_text("%s (%s)" % (_nd.display(person), + person.get_gramps_id())) + self.doc.end_paragraph() + + def __get_place_handles(self, places): + """ + This procedure converts a string of place GIDs to a list of handles + """ + place_handles = [] + for place_gid in places.split(): + place = self.database.get_place_from_gramps_id(place_gid) + place_handles.append(place.get_handle()) + place_handles.sort(self.sort.by_place_title) + return place_handles + +#------------------------------------------------------------------------ +# +# AncestorOptions +# +#------------------------------------------------------------------------ +class PlaceOptions(MenuReportOptions): + + """ + Defines options and provides handling interface. + """ + + def __init__(self, name, dbase): + MenuReportOptions.__init__(self, name, dbase) + + def add_menu_options(self, menu): + """ + Add options to the menu for the place report. + """ + category_name = _("Report Options") + + opt = FilterOption(_("Select using filter"), 0) + opt.set_help(_("Select places using a filter")) + filter_list = [] + filter_list.append(GenericFilter()) + filter_list.extend(CustomFilters.get_filters('Place')) + opt.set_filters(filter_list) + menu.add_option(category_name, "filter", opt) + + places = PlaceListOption(_("Select places individually")) + places.set_help(_("List of places to report on")) + menu.add_option(category_name, "places", places) + + def make_default_style(self, default_style): + """ + Make the default output style for the Place report. + """ + self.default_style = default_style + self.__report_title_style() + self.__place_title_style() + self.__place_details_style() + self.__column_title_style() + self.__section_style() + self.__event_table_style() + self.__details_style() + self.__cell_style() + self.__table_column_style() + + def __report_title_style(self): + """ + Define the style used for the report title + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SANS_SERIF, size=16, bold=1) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set_header_level(1) + para.set_top_margin(0.25) + para.set_bottom_margin(0.25) + para.set_alignment(BaseDoc.PARA_ALIGN_CENTER) + para.set_description(_('The style used for the title of the report.')) + self.default_style.add_paragraph_style("PLC-ReportTitle", para) + + def __place_title_style(self): + """ + Define the style used for the place title + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SERIF, size=12, italic=0, bold=1) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set(first_indent=-1.5, lmargin=1.5) + para.set_top_margin(0.75) + para.set_bottom_margin(0.25) + para.set_description(_('The style used for place title.')) + self.default_style.add_paragraph_style("PLC-PlaceTitle", para) + + def __place_details_style(self): + """ + Define the style used for the place details + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SERIF, size=10) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set(first_indent=0.0, lmargin=1.5) + para.set_description(_('The style used for place details.')) + self.default_style.add_paragraph_style("PLC-PlaceDetails", para) + + def __column_title_style(self): + """ + Define the style used for the event table column title + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SERIF, size=10, bold=1) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set(first_indent=-1.5, lmargin=1.5) + para.set_description(_('The style used for a column title.')) + self.default_style.add_paragraph_style("PLC-ColumnTitle", para) + + def __section_style(self): + """ + Define the style used for each section + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SERIF, size=10, italic=0, bold=0) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set(first_indent=-1.5, lmargin=1.5) + para.set_top_margin(0.5) + para.set_bottom_margin(0.25) + para.set_description(_('The style used for each section.')) + self.default_style.add_paragraph_style("PLC-Section", para) + + def __event_table_style(self): + """ + Define the style used for event table + """ + table = BaseDoc.TableStyle() + table.set_width(80) + table.set_columns(3) + table.set_column_width(0, 20) + table.set_column_width(1, 20) + table.set_column_width(2, 40) + self.default_style.add_table_style("PLC-EventTable", table) + + def __details_style(self): + """ + Define the style used for person and event details + """ + font = BaseDoc.FontStyle() + font.set(face=BaseDoc.FONT_SERIF, size=10) + para = BaseDoc.ParagraphStyle() + para.set_font(font) + para.set_description(_('The style used for event and person details.')) + self.default_style.add_paragraph_style("PLC-Details", para) + + def __cell_style(self): + """ + Define the style used for cells in the event table + """ + cell = BaseDoc.TableCellStyle() + self.default_style.add_cell_style("PLC-Cell", cell) + + def __table_column_style(self): + """ + Define the style used for event table columns + """ + cell = BaseDoc.TableCellStyle() + cell.set_bottom_border(1) + self.default_style.add_cell_style('PLC-TableColumn', cell) + +#------------------------------------------------------------------------ +# +# Register the plugin +# +#------------------------------------------------------------------------ +pmgr = PluginManager.get_instance() +pmgr.register_report( + name = 'place_report', + category = CATEGORY_TEXT, + report_class = PlaceReport, + options_class = PlaceOptions, + modes = MODE_GUI | MODE_BKI | MODE_CLI, + translated_name = _("Place Report"), + status = _("Stable"), + description = _("Produces a textual place report"), + author_name = "Gary Burton", + author_email = "gary.burton@zen.co.uk" + )