gramps/src/plugins/tool/EventCmp.py

453 lines
16 KiB
Python
Raw Normal View History

2002-10-20 19:55:16 +05:30
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2008 Brian G. Matherly
# Copyright (C) 2010 Jakim Friant
2002-10-20 19:55:16 +05:30
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""Tools/Analysis and Exploration/Compare Individual Events"""
2002-10-20 19:55:16 +05:30
#------------------------------------------------------------------------
#
# python modules
2002-10-20 19:55:16 +05:30
#
#------------------------------------------------------------------------
import os
from collections import defaultdict
2002-10-20 19:55:16 +05:30
#------------------------------------------------------------------------
#
# GNOME/GTK modules
#
#------------------------------------------------------------------------
import gtk
2002-10-20 19:55:16 +05:30
#------------------------------------------------------------------------
#
# GRAMPS modules
#
#------------------------------------------------------------------------
from Filters import GenericFilter, build_filter_model, Rules
import Sort
import Utils
from gui.utils import ProgressMeter
from docgen import ODSTab
import const
import Errors
import DateHandler
from QuestionDialog import WarningDialog
from gui.plug import tool
from gen.plug.report import utils as ReportUtils
import GrampsDisplay
import ManagedWindow
from gen.ggettext import sgettext as _
from glade import Glade
from gui.filtereditor import FilterEditor
#-------------------------------------------------------------------------
#
# Constants
#
#-------------------------------------------------------------------------
WIKI_HELP_PAGE = '%s_-_Tools' % const.URL_MANUAL_PAGE
2008-10-11 12:10:15 +05:30
WIKI_HELP_SEC = _('manual|Compare_Individual_Events...')
2002-10-20 19:55:16 +05:30
#------------------------------------------------------------------------
#
# EventCmp
2002-10-20 19:55:16 +05:30
#
#------------------------------------------------------------------------
class TableReport(object):
"""
This class provides an interface for the spreadsheet table
used to save the data into the file.
"""
2002-10-20 19:55:16 +05:30
def __init__(self,filename,doc):
self.filename = filename
self.doc = doc
2002-10-20 19:55:16 +05:30
def initialize(self,cols):
self.doc.open(self.filename)
self.doc.start_page()
2002-10-20 19:55:16 +05:30
def finalize(self):
self.doc.end_page()
self.doc.close()
def write_table_data(self,data,skip_columns=[]):
2002-10-20 19:55:16 +05:30
self.doc.start_row()
index = -1
2002-10-20 19:55:16 +05:30
for item in data:
index += 1
if index not in skip_columns:
self.doc.write_cell(item)
2002-10-20 19:55:16 +05:30
self.doc.end_row()
def set_row(self,val):
self.row = val + 2
def write_table_head(self, data):
2002-10-20 19:55:16 +05:30
self.doc.start_row()
map(self.doc.write_cell, data)
2002-10-20 19:55:16 +05:30
self.doc.end_row()
#------------------------------------------------------------------------
#
#
2002-10-20 19:55:16 +05:30
#
#------------------------------------------------------------------------
class EventComparison(tool.Tool,ManagedWindow.ManagedWindow):
def __init__(self, dbstate, uistate, options_class, name, callback=None):
self.dbstate = dbstate
self.uistate = uistate
tool.Tool.__init__(self,dbstate, options_class, name)
ManagedWindow.ManagedWindow.__init__(self, uistate, [], self)
2002-10-20 19:55:16 +05:30
self.qual = 0
self.filterDialog = Glade(toplevel="filters")
self.filterDialog.connect_signals({
2002-10-20 19:55:16 +05:30
"on_apply_clicked" : self.on_apply_clicked,
"on_editor_clicked" : self.filter_editor_clicked,
"on_help_clicked" : self.on_help_clicked,
"destroy_passed_object" : self.close,
"on_write_table" : self.__dummy,
2002-10-20 19:55:16 +05:30
})
window = self.filterDialog.toplevel
window.show()
self.filters = self.filterDialog.get_object("filter_list")
self.label = _('Event comparison filter selection')
self.set_window(window,self.filterDialog.get_object('title'),
self.label)
2002-10-20 19:55:16 +05:30
self.on_filters_changed('Person')
uistate.connect('filters-changed', self.on_filters_changed)
self.show()
def __dummy(self, obj):
"""dummy callback, needed because widget is in same glade file
as another widget, so callbacks must be defined to avoid warnings.
"""
pass
def on_filters_changed(self, name_space):
if name_space == 'Person':
all_filter = GenericFilter()
all_filter.set_name(_("Entire Database"))
all_filter.add_rule(Rules.Person.Everyone([]))
self.filter_model = build_filter_model('Person', [all_filter])
self.filters.set_model(self.filter_model)
self.filters.set_active(0)
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
def build_menu_names(self, obj):
return (_("Filter selection"),_("Event Comparison tool"))
2002-10-20 19:55:16 +05:30
def filter_editor_clicked(self, obj):
try:
FilterEditor('Person',const.CUSTOM_FILTERS,
self.dbstate,self.uistate)
except Errors.WindowActiveError:
pass
def on_apply_clicked(self, obj):
cfilter = self.filter_model[self.filters.get_active()][1]
2002-10-20 19:55:16 +05:30
progress_bar = ProgressMeter(_('Comparing events'),'')
progress_bar.set_pass(_('Selecting people'),1)
plist = cfilter.apply(self.db,
self.db.iter_person_handles())
progress_bar.step()
progress_bar.close()
self.options.handler.options_dict['filter'] = self.filters.get_active()
2005-12-06 12:08:09 +05:30
# Save options
self.options.handler.save_options()
2002-10-20 19:55:16 +05:30
if len(plist) == 0:
WarningDialog(_("No matches were found"))
2002-10-20 19:55:16 +05:30
else:
DisplayChart(self.dbstate,self.uistate,plist,self.track)
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def by_value(first,second):
return cmp(second[0],first[0])
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
def fix(line):
l = line.strip().replace('&','&').replace('>','>')
return l.replace(l,'<','&lt;').replace(l,'"','&quot;')
2002-10-20 19:55:16 +05:30
#-------------------------------------------------------------------------
#
#
#
#-------------------------------------------------------------------------
class DisplayChart(ManagedWindow.ManagedWindow):
def __init__(self,dbstate,uistate,people_list,track):
self.dbstate = dbstate
self.uistate = uistate
ManagedWindow.ManagedWindow.__init__(self, uistate, track, self)
self.db = dbstate.db
2002-10-20 19:55:16 +05:30
self.my_list = people_list
self.row_data = []
self.save_form = None
self.topDialog = Glade()
self.topDialog.connect_signals({
2002-10-20 19:55:16 +05:30
"on_write_table" : self.on_write_table,
"destroy_passed_object" : self.close,
"on_help_clicked" : self.on_help_clicked,
"on_apply_clicked" : self.__dummy,
"on_editor_clicked" : self.__dummy,
2002-10-20 19:55:16 +05:30
})
window = self.topDialog.toplevel
window.show()
self.set_window(window, self.topDialog.get_object('title'),
_('Event Comparison Results'))
self.eventlist = self.topDialog.get_object('treeview')
self.sort = Sort.Sort(self.db)
self.my_list.sort(self.sort.by_last_name)
2002-10-20 19:55:16 +05:30
self.event_titles = self.make_event_titles()
self.table_titles = [_("Person"),_("ID")]
for event_name in self.event_titles:
self.table_titles.append(_("%(event_name)s Date") %
{'event_name' :event_name}
)
self.table_titles.append('sort') # This won't be shown in a tree
self.table_titles.append(_("%(event_name)s Place") %
{'event_name' :event_name}
)
2002-10-20 19:55:16 +05:30
self.build_row_data()
self.draw_display()
self.show()
def __dummy(self, obj):
"""dummy callback, needed because widget is in same glade file
as another widget, so callbacks must be defined to avoid warnings.
"""
pass
def on_help_clicked(self, obj):
"""Display the relevant portion of GRAMPS manual"""
GrampsDisplay.help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC)
def build_menu_names(self, obj):
return (_("Event Comparison Results"),None)
2002-10-20 19:55:16 +05:30
def draw_display(self):
2002-10-20 19:55:16 +05:30
model_index = 0
tree_index = 0
mylist = []
renderer = gtk.CellRendererText()
for title in self.table_titles:
mylist.append(str)
if title == 'sort':
# This will override the previously defined column
self.eventlist.get_column(
tree_index-1).set_sort_column_id(model_index)
else:
column = gtk.TreeViewColumn(title,renderer,text=model_index)
column.set_sort_column_id(model_index)
self.eventlist.append_column(column)
# This one numbers the tree columns: increment on new column
tree_index += 1
# This one numbers the model columns: always increment
model_index += 1
model = gtk.ListStore(*mylist)
self.eventlist.set_model(model)
self.progress_bar.set_pass(_('Building display'),len(self.row_data))
for data in self.row_data:
model.append(row=list(data))
self.progress_bar.step()
self.progress_bar.close()
2002-10-20 19:55:16 +05:30
def build_row_data(self):
self.progress_bar = ProgressMeter(_('Comparing Events'),'')
self.progress_bar.set_pass(_('Building data'),len(self.my_list))
for individual_id in self.my_list:
individual = self.db.get_person_from_handle(individual_id)
name = individual.get_primary_name().get_name()
gid = individual.get_gramps_id()
the_map = defaultdict(list)
2006-03-31 09:49:06 +05:30
for ievent_ref in individual.get_event_ref_list():
ievent = self.db.get_event_from_handle(ievent_ref.ref)
event_name = str(ievent.get_type())
the_map[event_name].append(ievent_ref.ref)
2002-10-20 19:55:16 +05:30
first = True
done = False
while not done:
added = False
tlist = [name, gid] if first else ["", ""]
for ename in self.event_titles:
if ename in the_map and len(the_map[ename]) > 0:
event_handle = the_map[ename][0]
del the_map[ename][0]
date = place = ""
if event_handle:
event = self.db.get_event_from_handle(event_handle)
date = DateHandler.get_date(event)
sortdate = "%09d" % (
event.get_date_object().get_sort_value()
)
place_handle = event.get_place_handle()
if place_handle:
place = self.db.get_place_from_handle(
place_handle).get_title()
tlist += [date, sortdate, place]
added = True
2002-10-20 19:55:16 +05:30
else:
tlist += [""]*3
2002-10-20 19:55:16 +05:30
if first:
first = False
self.row_data.append(tlist)
elif not added:
done = True
2002-10-20 19:55:16 +05:30
else:
self.row_data.append(tlist)
self.progress_bar.step()
2002-10-20 19:55:16 +05:30
def make_event_titles(self):
"""
Create the list of unique event types, along with the person's
name, birth, and death.
This should be the column titles of the report.
"""
the_map = defaultdict(int)
for individual_id in self.my_list:
individual = self.db.get_person_from_handle(individual_id)
for event_ref in individual.get_event_ref_list():
2006-03-31 09:49:06 +05:30
event = self.db.get_event_from_handle(event_ref.ref)
name = str(event.get_type())
2002-10-20 19:55:16 +05:30
if not name:
break
the_map[name] += 1
2002-10-20 19:55:16 +05:30
unsort_list = sorted([(d, k) for k,d in the_map.iteritems()],by_value)
sort_list = [ item[1] for item in unsort_list ]
## Presently there's no Birth and Death. Instead there's Birth Date and
## Birth Place, as well as Death Date and Death Place.
## # Move birth and death to the begining of the list
## if _("Death") in the_map:
## sort_list.remove(_("Death"))
## sort_list = [_("Death")] + sort_list
## if _("Birth") in the_map:
## sort_list.remove(_("Birth"))
## sort_list = [_("Birth")] + sort_list
2002-10-20 19:55:16 +05:30
return sort_list
2002-10-20 19:55:16 +05:30
def on_write_table(self, obj):
f = gtk.FileChooserDialog(_("Select filename"),
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE,
gtk.RESPONSE_OK))
f.set_current_folder(os.getcwd())
status = f.run()
f.hide()
if status == gtk.RESPONSE_OK:
name = Utils.get_unicode_path_from_file_chooser(f.get_filename())
doc = ODSTab(len(self.row_data))
doc.creator(self.db.get_researcher().get_name())
spreadsheet = TableReport(name, doc)
new_titles = []
skip_columns = []
index = 0
for title in self.table_titles:
if title == 'sort':
skip_columns.append(index)
else:
new_titles.append(title)
index += 1
spreadsheet.initialize(len(new_titles))
spreadsheet.write_table_head(new_titles)
index = 0
for top in self.row_data:
spreadsheet.set_row(index%2)
index += 1
spreadsheet.write_table_data(top,skip_columns)
spreadsheet.finalize()
f.destroy()
2002-10-20 19:55:16 +05:30
2005-12-06 12:08:09 +05:30
#------------------------------------------------------------------------
#
#
2005-12-06 12:08:09 +05:30
#
#------------------------------------------------------------------------
class EventComparisonOptions(tool.ToolOptions):
2005-12-06 12:08:09 +05:30
"""
Defines options and provides handling interface.
"""
def __init__(self, name,person_id=None):
tool.ToolOptions.__init__(self, name,person_id)
2005-12-06 12:08:09 +05:30
# Options specific for this report
self.options_dict = {
'filter' : 0,
}
filters = ReportUtils.get_person_filters(None)
self.options_help = {
'filter' : ("=num","Filter number.",
[ filt.get_name() for filt in filters ],
True ),
2005-12-06 12:08:09 +05:30
}