diff --git a/gramps/plugins/tool/removespaces.glade b/gramps/plugins/tool/removespaces.glade new file mode 100644 index 000000000..05378ffb2 --- /dev/null +++ b/gramps/plugins/tool/removespaces.glade @@ -0,0 +1,235 @@ + + + + + + False + 650 + 400 + dialog + + + + + + + True + False + vertical + + + True + False + end + + + gtk-help + True + True + True + False + True + True + + + + False + False + 1 + + + + + gtk-close + True + True + True + False + True + right + True + + + + False + False + end + 1 + + + + + False + True + end + 0 + + + + + True + False + 6 + vertical + 6 + + + 1 + True + False + False + center + start + + + False + False + 5 + 0 + + + + + True + False + 2 + 2 + vertical + 5 + True + top + + + True + False + vertical + + + True + False + vertical + + + True + False + label + + + False + True + 0 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + + + + + + + + True + True + 1 + + + + + False + True + 0 + + + + + True + False + vertical + + + True + False + vertical + + + True + False + label + + + False + True + 0 + + + + + False + True + 0 + + + + + True + True + in + + + True + True + + + + + + + + True + True + 1 + + + + + False + True + 1 + + + + + True + True + 1 + + + + + True + True + 1 + + + + + + close + + + diff --git a/gramps/plugins/tool/removespaces.py b/gramps/plugins/tool/removespaces.py new file mode 100644 index 000000000..952a94fe0 --- /dev/null +++ b/gramps/plugins/tool/removespaces.py @@ -0,0 +1,315 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-2009 Stephane Charette +# Copyright (C) 2019- Serge Noiraud +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +"Find possible leading and/or trailing spaces in places name and people" + +#------------------------------------------------------------------------ +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------ +from gi.repository import Gtk +from gi.repository import GObject + +#------------------------------------------------------------------------ +# +# Gramps modules +# +#------------------------------------------------------------------------ +from gramps.gen.const import URL_MANUAL_PAGE +from gramps.gui.plug import tool +from gramps.gui.editors import (EditPlace, EditPerson) +from gramps.gen.errors import WindowActiveError +from gramps.gui.managedwindow import ManagedWindow +from gramps.gui.utils import ProgressMeter +from gramps.gui.display import display_help +from gramps.gui.glade import Glade +from gramps.gen.const import GRAMPS_LOCALE as glocale +_ = glocale.translation.sgettext + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +WIKI_HELP_PAGE = '%s_-_Tools' % URL_MANUAL_PAGE +WIKI_HELP_SEC = _('manual|Remove_leading_and_trailing_spaces') + +#------------------------------------------------------------------------ +# +# RemoveSpaces class +# +#------------------------------------------------------------------------ +class RemoveSpaces(ManagedWindow): + """ + Find leading and trailing spaces in Place names and person names + """ + def __init__(self, dbstate, user, options_class, name, callback=None): + uistate = user.uistate + dummy_opt = options_class + dummy_nme = name + dummy_cb = callback + + self.title = _('Clean input data') + ManagedWindow.__init__(self, uistate, [], self.__class__) + self.dbstate = dbstate + self.uistate = uistate + self.db = dbstate.db + + top_dialog = Glade() + + top_dialog.connect_signals({ + "destroy_passed_object" : self.close, + "on_help_clicked" : self.on_help_clicked, + "on_delete_event" : self.close, + }) + + window = top_dialog.toplevel + title = top_dialog.get_object("title") + self.set_window(window, title, self.title) + tip = _('Search leading and/or trailing spaces for persons' + ' and places. Search comma in coordinates fields.\n' + 'Double click on a row to edit its content.') + title.set_tooltip_text(tip) + + # start the progress indicator + self.progress = ProgressMeter(self.title, _('Starting'), + parent=uistate.window) + steps = self.db.get_number_of_people() + self.db.get_number_of_places() + self.progress.set_pass(_('Looking for possible fields with leading or' + ' trailing spaces'), steps) + + self.model_1 = Gtk.ListStore( + GObject.TYPE_STRING, # 0==handle + GObject.TYPE_STRING, # 1==firstname + GObject.TYPE_STRING, # 2==surname + GObject.TYPE_STRING, # 3==alternate name + GObject.TYPE_STRING, # 4==group_as + ) + self.model_1.set_sort_column_id( + Gtk.TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 1) + + self.label_1 = top_dialog.get_object("label_1") + self.label_1.set_text(_('Person')) + self.treeview_1 = top_dialog.get_object("treeview_1") + self.treeview_1.set_model(self.model_1) + col1 = Gtk.TreeViewColumn(_('handle'), + Gtk.CellRendererText(), text=0) + renderer1 = Gtk.CellRendererText() + renderer1.set_property('underline-set', True) + renderer1.set_property('underline', 2) # 2=double underline + col2 = Gtk.TreeViewColumn(_('firstname'), renderer1, text=1) + renderer2 = Gtk.CellRendererText() + renderer2.set_property('underline-set', True) + renderer2.set_property('underline', 2) # 2=double underline + col3 = Gtk.TreeViewColumn(_('surname'), renderer2, text=2) + renderer3 = Gtk.CellRendererText() + renderer3.set_property('underline-set', True) + renderer3.set_property('underline', 2) # 2=double underline + col4 = Gtk.TreeViewColumn(_('alternate name'), renderer3, text=3) + renderer4 = Gtk.CellRendererText() + renderer4.set_property('underline-set', True) + renderer4.set_property('underline', 2) # 2=double underline + col5 = Gtk.TreeViewColumn(_('group as'), renderer4, text=4) + col1.set_resizable(True) + col1.set_visible(False) + col2.set_resizable(True) + col3.set_resizable(True) + col4.set_resizable(True) + col5.set_resizable(True) + col1.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col2.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col3.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col4.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col5.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + self.treeview_1.append_column(col1) + self.treeview_1.append_column(col2) + self.treeview_1.append_column(col3) + self.treeview_1.append_column(col4) + self.treeview_1.append_column(col5) + self.treeselection = self.treeview_1.get_selection() + self.treeview_1.connect('row-activated', self.rowactivated_cb1) + + self.model_2 = Gtk.ListStore( + GObject.TYPE_STRING, # 0==handle + GObject.TYPE_STRING, # 1==name + GObject.TYPE_STRING, # 2==latitude + GObject.TYPE_STRING) # 3==longitude + self.model_2.set_sort_column_id( + Gtk.TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 1) + + self.label_2 = top_dialog.get_object("label_2") + self.label_2.set_text(_('Place')) + self.treeview_2 = top_dialog.get_object("treeview_2") + self.treeview_2.set_model(self.model_2) + col1 = Gtk.TreeViewColumn(_('handle'), + Gtk.CellRendererText(), text=0) + renderer5 = Gtk.CellRendererText() + renderer5.set_property('underline-set', True) + renderer5.set_property('underline', 2) # 2=double underline + col2 = Gtk.TreeViewColumn(_('name'), renderer5, text=1) + renderer6 = Gtk.CellRendererText() + renderer6.set_property('underline-set', True) + renderer6.set_property('underline', 2) # 2=double underline + col3 = Gtk.TreeViewColumn(_('latitude'), renderer6, text=2) + renderer7 = Gtk.CellRendererText() + renderer7.set_property('underline-set', True) + renderer7.set_property('underline', 2) # 2=double underline + col4 = Gtk.TreeViewColumn(_('longitude'), renderer7, text=3) + col1.set_resizable(True) + col1.set_visible(False) + col2.set_resizable(True) + col3.set_resizable(True) + col4.set_resizable(True) + col1.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col2.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col3.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + col4.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + self.treeview_2.append_column(col1) + self.treeview_2.append_column(col2) + self.treeview_2.append_column(col3) + self.treeview_2.append_column(col4) + self.treeselection = self.treeview_2.get_selection() + self.treeview_2.connect('row-activated', self.rowactivated_cb2) + + self.places() + self.people() + + # close the progress bar + self.progress.close() + + self.show() + + def places(self): + """ + For all places in the database, if the name contains leading or + trailing spaces. + """ + for place_handle in self.db.get_place_handles(): + self.progress.step() + place = self.db.get_place_from_handle(place_handle) + place_name = place.get_name() + pname = place_name.get_value() + found = False + if pname != pname.strip(): + found = True + plat = place.get_latitude() + if plat != plat.strip(): + found = True + if plat.find(',') != -1: + found = True + plon = place.get_longitude() + if plon != plon.strip(): + found = True + if plon.find(',') != -1: + found = True + if found: + value = (place_handle, pname, plat, plon) + self.model_2.append(value) + return True + + def people(self): + """ + For all persons in the database, if the name contains leading or + trailing spaces. Works for alternate names and group_as. + """ + for person_handle in self.db.get_person_handles(): + self.progress.step() + person = self.db.get_person_from_handle(person_handle) + primary_name = person.get_primary_name() + fname = primary_name.get_first_name() + found = False + if fname != fname.strip(): + found = True + sname = primary_name.get_primary_surname().get_surname() + if sname != sname.strip(): + found = True + paname = "" + for name in primary_name.get_surname_list(): + aname = name.get_surname() + if aname != sname and aname != aname.strip(): + found = True + if paname != "": + paname += ', ' + paname += aname + groupas = primary_name.group_as + if groupas != groupas.strip(): + found = True + if found: + value = (person_handle, fname, sname, paname, groupas) + self.model_1.append(value) + return True + + def rowactivated_cb1(self, treeview, path, column): + """ + Called when a Person row is activated. + """ + dummy_tv = treeview + dummy_col = column + iter_ = self.model_1.get_iter(path) + handle = self.model_1.get_value(iter_, 0) + person = self.dbstate.db.get_person_from_handle(handle) + if person: + try: + EditPerson(self.dbstate, self.uistate, [], person) + except WindowActiveError: + pass + return True + return False + + def rowactivated_cb2(self, treeview, path, column): + """ + Called when a Place row is activated. + """ + dummy_tv = treeview + dummy_col = column + iter_ = self.model_2.get_iter(path) + handle = self.model_2.get_value(iter_, 0) + place = self.dbstate.db.get_place_from_handle(handle) + if place: + try: + EditPlace(self.dbstate, self.uistate, [], place) + except WindowActiveError: + pass + return True + return False + + def on_help_clicked(self, _obj): + """ + Display the relevant portion of Gramps manual. + """ + display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC) + + def close(self, *obj): + ManagedWindow.close(self, *obj) + +#------------------------------------------------------------------------ +# +# RemoveSpacesOptions +# +#------------------------------------------------------------------------ +class RemoveSpacesOptions(tool.ToolOptions): + """ + Defines options and provides handling interface. + """ + def __init__(self, name, person_id=None): + """ Initialize the options class """ + tool.ToolOptions.__init__(self, name, person_id) diff --git a/gramps/plugins/tool/tools.gpr.py b/gramps/plugins/tool/tools.gpr.py index ec3ef498c..fdd362904 100644 --- a/gramps/plugins/tool/tools.gpr.py +++ b/gramps/plugins/tool/tools.gpr.py @@ -476,3 +476,28 @@ toolclass = 'FindLoop', optionclass = 'FindLoopOptions', tool_modes = [TOOL_MODE_GUI] ) + +#------------------------------------------------------------------------ +# +# Remove leading and trailing spaces for places name +# Remove leading and trailing spaces for surname and first names +# +#------------------------------------------------------------------------ + +register(TOOL, +id = 'removespaces', +name = _("Clean input data"), +description = _("Searches the entire database, looking for " + "trailing or leading spaces for places and people." + " Search comma in coordinates fields in places."), +version = '1.0', +gramps_target_version = MODULE_VERSION, +status = STABLE, +fname = 'removespaces.py', +authors = ["Serge Noiraud"], +authors_email = ["serge.noiraud@free.fr"], +category = TOOL_UTILS, +toolclass = 'RemoveSpaces', +optionclass = 'RemoveSpacesOptions', +tool_modes = [TOOL_MODE_GUI] + )