From 022da0cb82437b02f3d055149381985d3efe7938 Mon Sep 17 00:00:00 2001 From: Alois Poettker Date: Sat, 13 May 2017 17:34:16 +0200 Subject: [PATCH] Extend ReorderIDs-II functionality. (#383) --- gramps/plugins/tool/reorderids.glade | 1596 ++++++++++++++++++++++++++ gramps/plugins/tool/reorderids.py | 730 +++++++++--- po/POTFILES.in | 1 + 3 files changed, 2156 insertions(+), 171 deletions(-) create mode 100644 gramps/plugins/tool/reorderids.glade diff --git a/gramps/plugins/tool/reorderids.glade b/gramps/plugins/tool/reorderids.glade new file mode 100644 index 000000000..3cd6ef2c9 --- /dev/null +++ b/gramps/plugins/tool/reorderids.glade @@ -0,0 +1,1596 @@ + + + + + + False + False + True + center-on-parent + True + dialog + False + + + True + False + vertical + + + True + False + 10 + 5 + + + + + + False + True + 0 + + + + + True + False + end + center + 5 + 4 + 2 + True + + + 1 + -1 + True + False + True + 8 + False + False + False + False + + + + 6 + 1 + 1 + 1 + + + + + True + False + True + 8 + False + False + False + False + + + + 6 + 2 + 1 + 1 + + + + + True + False + True + 8 + False + False + False + False + + + + 6 + 3 + 1 + 1 + + + + + True + False + True + 8 + False + False + False + False + + + + 6 + 4 + 1 + 1 + + + + + True + False + True + 8 + False + False + False + False + + + + 6 + 5 + 1 + 1 + + + + + True + False + True + 8 + False + False + False + False + + + + 6 + 6 + 1 + 1 + + + + + True + False + True + 8 + + + + 6 + 7 + 1 + 1 + + + + + True + False + True + 8 + + + + 6 + 8 + 1 + 1 + + + + + True + False + True + 8 + + + + 6 + 9 + 1 + 1 + + + + + 10 + True + False + True + 4 + False + False + False + False + + + 7 + 1 + 1 + 1 + + + + + True + False + True + 4 + False + False + False + False + digits + + + 7 + 2 + 1 + 1 + + + + + True + False + True + 4 + False + False + False + False + digits + + + 7 + 3 + 1 + 1 + + + + + True + False + True + 4 + False + False + False + False + number + + + 7 + 4 + 1 + 1 + + + + + True + False + True + 4 + False + False + False + False + number + + + 7 + 5 + 1 + 1 + + + + + True + False + True + 4 + False + False + False + False + number + + + 7 + 6 + 1 + 1 + + + + + True + False + True + 4 + number + + + 7 + 7 + 1 + 1 + + + + + True + False + True + 4 + number + + + 7 + 8 + 1 + 1 + + + + + True + False + True + 4 + number + + + 7 + 9 + 1 + 1 + + + + + True + True + start + False + 8 + False + + + 2 + 1 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 2 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 3 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 4 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 5 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 6 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 7 + 1 + 1 + + + + + True + True + False + 8 + False + + + 2 + 8 + 1 + 1 + + + + + 0 + True + True + False + 8 + False + + + 2 + 9 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 1 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 2 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 3 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 4 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 5 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 6 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 7 + 1 + 1 + + + + + True + True + False + 6 + False + + + 3 + 8 + 1 + 1 + + + + + 0 + True + True + False + 6 + False + + + 3 + 9 + 1 + 1 + + + + + True + False + start + Person + 1 + + + 1 + 1 + 1 + 1 + + + + + True + False + start + Family + 0 + + + 1 + 2 + 1 + 1 + + + + + True + False + start + Event + + + 1 + 3 + 1 + 1 + + + + + True + False + start + Place + + + 1 + 4 + 1 + 1 + + + + + True + False + start + Source + + + 1 + 5 + 1 + 1 + + + + + True + False + start + Citation + + + 1 + 6 + 1 + 1 + + + + + True + False + start + Repository + 12 + + + 1 + 7 + 1 + 1 + + + + + True + False + start + Media + + + 1 + 8 + 1 + 1 + + + + + True + False + start + Note + + + 1 + 9 + 1 + 1 + + + + + True + True + False + queue + 0 + True + True + + + + 0 + 1 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 2 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 3 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 4 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 5 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 6 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 7 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 8 + 1 + 1 + + + + + True + True + False + 0 + True + True + + + + 0 + 9 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 1 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 2 + 1 + 1 + + + + + True + True + 8 + out + + + + + 4 + 3 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 4 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 5 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 6 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 7 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 8 + 1 + 1 + + + + + True + True + 8 + + + + + 4 + 9 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 1 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 2 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 3 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 4 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 5 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 6 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 7 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 8 + 1 + 1 + + + + + True + False + True + False + center + 0 + True + + + 8 + 9 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 1 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 2 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 3 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 4 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 5 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 6 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 7 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 8 + 1 + 1 + + + + + True + True + False + center + 0 + True + + + + 5 + 9 + 1 + 1 + + + + + Object + True + True + False + Enable ID reordering. + start + none + False + + + + 1 + 0 + 1 + 1 + + + + + True + False + List next ID available +(maynot be continuous). + start + Actual + fill + + + 2 + 0 + 1 + 1 + + + + + True + False + Amount of ID in use. + start + Quantity + + + 3 + 0 + 1 + 1 + + + + + Format + True + True + False + Actual / Upcoming ID format. + start + none + False + + + + 4 + 0 + 1 + 1 + + + + + Change + True + True + True + Enable ID reordering +with Start / Step sequence. + immediate + none + False + + + + 5 + 0 + 1 + 1 + + + + + Start + True + True + False + Reorder ID start number. + start + none + False + + + + 6 + 0 + 1 + 1 + + + + + Step + True + True + True + Reorder ID step width. + start + none + False + + + + 7 + 0 + 1 + 1 + + + + + Keep + True + True + True + Keep IDs with alternate +prefixes untouched. + none + False + + + + 8 + 0 + 1 + 1 + + + + + + + + False + True + 1 + + + + + True + False + 5 + 5 + end + + + gtk-cancel + False + True + True + True + False + True + True + + + + False + False + 0 + + + + + gtk-ok + False + True + True + True + False + True + True + + + + False + False + 1 + + + + + gtk-help + False + True + True + True + False + True + True + + + + False + False + 2 + + + + + True + True + end + 3 + + + + + + reorder_cancel + reorder_ok + reorder_help + + + diff --git a/gramps/plugins/tool/reorderids.py b/gramps/plugins/tool/reorderids.py index c7340f653..2bfb4e987 100644 --- a/gramps/plugins/tool/reorderids.py +++ b/gramps/plugins/tool/reorderids.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# Copyright (C) 2017 Alois Poettker # # 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 @@ -21,7 +22,7 @@ # """ -Change id IDs of all the elements in the database to conform to the +Change IDs of all elements in the database to conform to the scheme specified in the database's prefix ids """ @@ -31,211 +32,600 @@ scheme specified in the database's prefix ids # #------------------------------------------------------------------------ import re -from gramps.gen.const import GRAMPS_LOCALE as glocale -_ = glocale.translation.gettext + +from gi.repository import Gtk, Gdk #------------------------------------------------------------------------ # # Gramps modules # #------------------------------------------------------------------------ -from gramps.gui.utils import ProgressMeter -from gramps.gen.lib import (Event, Family, Media, Note, - Person, Place, Repository, Source, Citation) +from gramps.gen.const import URL_MANUAL_PAGE +from gramps.gen.const import GRAMPS_LOCALE as glocale +_ = glocale.translation.gettext + +from gramps.gen.config import config from gramps.gen.db import DbTxn +from gramps.gen.lib import (Person, Family, Event, Place, + Source, Citation, Repository, + Media, Note) +from gramps.gen.updatecallback import UpdateCallback + +from gramps.gui.display import display_help +from gramps.gui.glade import Glade +from gramps.gui.managedwindow import ManagedWindow from gramps.gui.plug import tool +from gramps.gui.utils import ProgressMeter +from gramps.gui.widgets import MonitoredCheckbox, MonitoredEntry -_findint = re.compile('^[^\d]*(\d+)[^\d]*') - -# gets the number specified in a format string, example: %04d returns '04' -_parseformat = re.compile('.*%(\d+)[^\d]+') +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +WIKI_HELP_PAGE = '%s_-_Tools' % URL_MANUAL_PAGE +WIKI_HELP_SEC = _('manual|Reorder_Gramps_ID') #------------------------------------------------------------------------- # # Actual tool # #------------------------------------------------------------------------- -class ReorderIds(tool.BatchTool): - def __init__(self, dbstate, user, options_class, name, callback=None): - uistate = user.uistate - tool.BatchTool.__init__(self, dbstate, user, options_class, name) - if self.fail: - return - db = dbstate.db - self.uistate = uistate - if uistate: - self.progress = ProgressMeter( - _('Reordering Gramps IDs'), '', parent=uistate.window) +# gets the number specified in a format string, eg: %04d returns '04' +_parseformat = re.compile('.*%(\d+)[^\d]+') + +class ReorderEntry(object): + """ Class for internal values for every primary object """ + + def __init__(self, object_fmt, quant_id, nextgramps_id): + self.width_fmt = 4 + self.object_fmt, self.object_prefix = '', '' + self.quant_id, self.actual_id, self.actgramps_id = 0, 0, '0' + self.calc_id(object_fmt, quant_id) + self.stored_fmt = object_fmt + self.stored_prefix = self.object_prefix + + self.number_id = int(nextgramps_id.split(self.object_prefix, 1)[1]) + self.step_id = 1 + self.active_obj, self.change_obj, self.keep_obj = True, False, False + + def set_active(self, active): + """ sets Change flag """ + self.active_obj = active + + def get_active(self): + """ gets Change flag """ + return self.active_obj + + def set_fmt(self, object_fmt): + """ sets primary object format """ + if object_fmt: + self.calc_id(object_fmt, self.quant_id) + + def get_fmt(self): + """ gets primary object format """ + return self.object_fmt + + def res_fmt(self): + """ restore primary object format """ + return self.stored_fmt + + def set_change(self, change): + """ sets Change flag """ + self.change_obj = change + + def get_change(self): + """ gets Change flag """ + return self.change_obj + + def __ret_gid(self, actual): + """ return Gramps ID in correct format """ + return '%s%s' % \ + (self.object_prefix, str(actual).zfill(self.width_fmt)) + + def calc_id(self, object_fmt, quant_id): + """ calculates identifier prefix, format & actual value """ + self.object_prefix = object_fmt.split('%', 1)[0] if '%' in object_fmt else '' + self.object_fmt, self.quant_id = object_fmt, quant_id + + # Default values, ID counting starts with zero! + self.width_fmt, self.actual_id = 4, 0 + formatmatch = _parseformat.match(object_fmt) + if formatmatch: + self.width_fmt = int(formatmatch.groups()[0]) + self.actgramps_id = self.__ret_gid(self.actual_id) + + def zero_id(self): + """ provide zero Start ID """ + return self.__ret_gid(0) + + def set_id(self, actual): + """ sets Start ID """ + text = ''.join([i for i in actual.strip() if i in '0123456789']) + self.actual_id = int(text) if text else 0 + + def get_id(self): + """ gets Start ID """ + return self.__ret_gid(self.actual_id) + + def next_id(self): + """ provide next Start ID """ + return self.__ret_gid(self.number_id) + + def succ_id(self): + """ provide next actual Gramps ID """ + self.actual_id += self.step_id + self.actgramps_id = self.__ret_gid(self.actual_id) + + return self.actgramps_id + + def last_id(self): + """ provide quantities of Gramps IDs """ + if self.quant_id > 0: + return self.__ret_gid(self.quant_id -1) else: - print(_("Reordering Gramps IDs...")) + return self.__ret_gid(0) - with DbTxn(_("Reorder Gramps IDs"), db, batch=True) as self.trans: - db.disable_signals() + def set_step(self, step): + """ sets ID Step width """ + text = ''.join([i for i in step.strip() if i in '0123456789']) + self.step_id = int(text) if text else 1 - if uistate: - self.progress.set_pass(_('Reordering People IDs'), - db.get_number_of_people()) - self.reorder(Person, - db.get_person_from_gramps_id, - db.get_person_from_handle, - db.find_next_person_gramps_id, - db.get_person_handles, - db.commit_person, - db.person_prefix) + def get_step(self): + """ gets ID Step width """ + return str(self.step_id) - if uistate: - self.progress.set_pass(_('Reordering Family IDs'), - db.get_number_of_families()) - self.reorder(Family, - db.get_family_from_gramps_id, - db.get_family_from_handle, - db.find_next_family_gramps_id, - db.get_family_handles, - db.commit_family, - db.family_prefix) - if uistate: - self.progress.set_pass(_('Reordering Event IDs'), - db.get_number_of_events()) - self.reorder(Event, - db.get_event_from_gramps_id, - db.get_event_from_handle, - db.find_next_event_gramps_id, - db.get_event_handles, - db.commit_event, - db.event_prefix) - if uistate: - self.progress.set_pass(_('Reordering Media Object IDs'), - db.get_number_of_media()) - self.reorder(Media, - db.get_media_from_gramps_id, - db.get_media_from_handle, - db.find_next_media_gramps_id, - db.get_media_handles, - db.commit_media, - db.media_prefix) - if uistate: - self.progress.set_pass(_('Reordering Source IDs'), - db.get_number_of_sources()) - self.reorder(Source, - db.get_source_from_gramps_id, - db.get_source_from_handle, - db.find_next_source_gramps_id, - db.get_source_handles, - db.commit_source, - db.source_prefix) - if uistate: - self.progress.set_pass(_('Reordering Citation IDs'), - db.get_number_of_citations()) - self.reorder(Citation, - db.get_citation_from_gramps_id, - db.get_citation_from_handle, - db.find_next_citation_gramps_id, - db.get_citation_handles, - db.commit_citation, - db.citation_prefix) - if uistate: - self.progress.set_pass(_('Reordering Place IDs'), - db.get_number_of_places()) - self.reorder(Place, - db.get_place_from_gramps_id, - db.get_place_from_handle, - db.find_next_place_gramps_id, - db.get_place_handles, - db.commit_place, - db.place_prefix) - if uistate: - self.progress.set_pass(_('Reordering Repository IDs'), - db.get_number_of_repositories()) - self.reorder(Repository, - db.get_repository_from_gramps_id, - db.get_repository_from_handle, - db.find_next_repository_gramps_id, - db.get_repository_handles, - db.commit_repository, - db.repository_prefix) - #add reorder notes ID - if uistate: - self.progress.set_pass(_('Reordering Note IDs'), - db.get_number_of_notes()) - self.reorder(Note, - db.get_note_from_gramps_id, - db.get_note_from_handle, - db.find_next_note_gramps_id, - db.get_note_handles, - db.commit_note, - db.note_prefix) - if uistate: - self.progress.close() + def change_step(self, step_entry): + """ change Glade Step entry """ + step_id = step_entry.get_text().strip() + if step_id and step_id != str(self.step_id): + step_entry.set_text(str(self.step_id)) + + def set_keep(self, keep): + """ sets Keep flag """ + self.keep_obj = keep + + def get_keep(self): + """ gets Keep flag """ + return self.keep_obj + +class ReorderIds(tool.BatchTool, ManagedWindow, UpdateCallback): + """ Class for Reodering Gramps ID Tool """ + xobjects = (('person', 'people'), ('family', 'families'), + ('event', 'events'), ('place', 'places'), + ('source', 'sources'), ('citation', 'citations'), + ('repository', 'repositories'), + ('media', 'media'), ('note', 'notes')) + + def build_menu_names_(self, widget=None): + """ The menu name """ + return (_('Main window'), _("Reorder Gramps IDs")) + + def __init__(self, dbstate, user, options_class, name, callback=None): + self.uistate = user.uistate + self.dbstate = dbstate.db + + if self.uistate: + tool.BatchTool.__init__(self, dbstate, user, options_class, name) + if self.fail: + return # user denied to modify Gramps IDs + + ManagedWindow.__init__(self, self.uistate, [], self.__class__) + if not self.uistate: + UpdateCallback.__init__(self, user.callback) + + self.object_status = True + self.change_status = False + self.start_zero = True + self.step_cnt, self.step_list = 0, ['1', '2', '5', '10'] + self.keep_status = True + + self.obj_values = {} # enable access to all internal values + self.active_entries, self.format_entries = {}, {} + self.change_entries = {} + self.start_entries, self.step_entries = {}, {} + self.keep_entries = {} + + self.prim_methods, self.obj_methods = {}, {} + for prim_obj, prim_objs in self.xobjects: + class_type = prim_obj.title() + table = "self.dbstate.%s_map" % prim_obj + get_number_obj = "self.dbstate.get_number_of_%s" % prim_objs + prefix_fmt = "self.dbstate.%s_prefix" % prim_obj + get_from_id = "self.dbstate.get_%s_from_gramps_id" % prim_obj + get_from_handle = "self.dbstate.get_%s_from_handle" % prim_obj + next_from_id = "self.dbstate.find_next_%s_gramps_id" % prim_obj + commit = "self.dbstate.commit_%s" % prim_obj + + self.prim_methods[prim_obj] = (eval(prefix_fmt), eval(get_number_obj)(), + eval(next_from_id)()) + self.obj_methods[prim_obj] = (eval(class_type), eval(table), eval(commit), + eval(get_from_id), eval(get_from_handle), + eval(next_from_id)) + + object_fmt, quant_id, next_id = self.prim_methods[prim_obj] + + obj_value = ReorderEntry(object_fmt, quant_id, next_id) + self.obj_values[prim_obj] = obj_value + + if self.uistate: + self._display() + else: + self._execute() + + def __on_object_button_clicked(self, widget=None): + """ compute all primary objects and toggle the 'Active' attribute """ + self.object_status = not self.object_status + + for prim_obj, tmp in self.xobjects: + obj = self.top.get_object('%s_active' % prim_obj) + obj.set_active(self.object_status) + + def __on_object_button_toggled(self, widget): + """ compute the primary object and toggle the 'Sensitive' attribute """ + obj_state = widget.get_active() + obj_name = Gtk.Buildable.get_name(widget).split('_', 1)[0] + + self.active_entries[obj_name].set_val(obj_state) + + for obj_entry in ['actual', 'quant', 'format', 'change']: + obj = self.top.get_object('%s_%s' % (obj_name, obj_entry)) + obj.set_sensitive(obj_state) + + for obj_entry in ['start', 'step', 'keep']: + obj = self.top.get_object('%s_change' % obj_name) + if obj.get_active(): + obj = self.top.get_object('%s_%s' % (obj_name, obj_entry)) + obj.set_sensitive(obj_state) + + def __on_format_button_clicked(self, widget=None): + """ compute all sensitive primary objects and sets the + 'Format' scheme of identifiers """ + for prim_obj, tmp in self.xobjects: + obj_format = self.top.get_object('%s_format' % prim_obj) + if not obj_format.get_sensitive(): + continue + + obj_fmt = self.obj_values[prim_obj].res_fmt() + self.format_entries[prim_obj].force_value(obj_fmt) + if self.start_zero: + obj_id = self.obj_values[prim_obj].zero_id() else: - print(_("Done.")) + obj_id = self.obj_values[prim_obj].last_id() + self.start_entries[prim_obj].force_value(obj_id) - db.enable_signals() - db.request_rebuild() + def __on_change_button_clicked(self, widget=None): + """ compute all primary objects and toggle the 'Change' attribute """ + self.change_status = not self.change_status - def reorder(self, class_type, find_from_id, find_from_handle, - find_next_id, find_handles, commit, prefix): - dups = [] - newids = {} + for prim_obj, tmp in self.xobjects: + obj_change = self.top.get_object('%s_change' % prim_obj) + if not obj_change.get_sensitive(): + continue - formatmatch = _parseformat.match(prefix) + self.change_entries[prim_obj].set_val(self.change_status) + obj_change.set_active(self.change_status) - for handle in find_handles(): + def __on_change_button_toggled(self, widget): + """ compute the primary object and toggle the 'Sensitive' attribute """ + obj_state = widget.get_active() + obj_name = Gtk.Buildable.get_name(widget).split('_', 1)[0] + + for obj_entry in ['start', 'step', 'keep']: + obj = self.top.get_object('%s_%s' % (obj_name, obj_entry)) + if obj_entry == 'keep': + if self.obj_values[obj_name].stored_prefix != \ + self.obj_values[obj_name].object_prefix: + self.keep_entries[obj_name].set_val(False) + else: + obj.set_active(obj_state) + self.keep_entries[obj_name].set_val(obj_state) + obj.set_sensitive(obj_state) + + def __on_start_button_clicked(self, widget=None): + """ compute all sensitive primary objects and sets the + 'Start' values of identifiers """ + self.start_zero = not self.start_zero + + for prim_obj, tmp in self.xobjects: + obj = self.top.get_object('%s_start' % prim_obj) + if not obj.get_sensitive(): + continue + + if self.start_zero: + obj_id = self.obj_values[prim_obj].zero_id() + else: + obj_id = self.obj_values[prim_obj].next_id() + self.start_entries[prim_obj].force_value(obj_id) + + def __on_step_button_clicked(self, widget=None): + """ compute all sensitive primary objects and sets the + 'Step' width of identifiers """ + self.step_cnt = self.step_cnt +1 if self.step_cnt < 3 else 0 + + for prim_obj, tmp in self.xobjects: + obj = self.top.get_object('%s_step' % prim_obj) + if not obj.get_sensitive(): + continue + + step_val = self.step_list[self.step_cnt] + self.step_entries[prim_obj].force_value(step_val) + + def __on_keep_button_clicked(self, widget=None): + """ compute the primary object and toggle the 'Active' attribute """ + self.keep_status = not self.keep_status + + for prim_obj, tmp in self.xobjects: + obj = self.top.get_object('%s_change' % prim_obj) + if not obj.get_active(): + continue + + obj = self.top.get_object('%s_keep' % prim_obj) + obj.set_active(self.keep_status) + self.keep_entries[prim_obj].set_val(self.keep_status) + + def __on_format_entry_keyrelease(self, widget, event, data=None): + """ activated on all return's of an entry """ + if event.keyval in [Gdk.KEY_Return]: + obj_name = Gtk.Buildable.get_name(widget).split('_', 1)[0] + obj_fmt = self.format_entries[obj_name].get_val() + self.format_entries[obj_name].force_value(obj_fmt) + self.start_entries[obj_name].update() + + obj_change = self.top.get_object('%s_change' % obj_name) + obj_change.grab_focus() + + return False + + def __on_format_entry_focusout(self, widget, event, data=None): + """ activated on all focus out of an entry """ + obj_name = Gtk.Buildable.get_name(widget).split('_', 1)[0] + obj_fmt = self.format_entries[obj_name].get_val() + + self.format_entries[obj_name].set_val(obj_fmt) + self.start_entries[obj_name].update() + + return False + + def __on_start_entry_focusout(self, widget, event, data=None): + """ activated on all focus out of an entry """ + obj_name = Gtk.Buildable.get_name(widget).split('_', 1)[0] + self.start_entries[obj_name].update() + + return False + + def __on_ok_button_clicked(self, widget=None): + """ execute the reodering and close """ + self._execute() + self._update() + + self.close() + + def __on_cancel_button_clicked(self, widget=None): + """ cancel the reodering and close """ + self.close() + + def __on_help_button_clicked(self, widget=None): + """ display the relevant portion of Gramps manual """ + display_help(webpage=WIKI_HELP_PAGE, section=WIKI_HELP_SEC) + + def _display(self): + """ organize Glade 'Reorder IDs' window """ + + # get the main window from glade + self.top = Glade(toplevel="reorder-ids") + window = self.top.toplevel + + # set gramps style title for the window + self.set_window(window, self.top.get_object("title"), \ + _("Reorder Gramps IDs")) + + # connect signals + self.top.connect_signals({ + "on_object_button_clicked" : self.__on_object_button_clicked, + "on_object_button_toggled" : self.__on_object_button_toggled, + "on_format_button_clicked" : self.__on_format_button_clicked, + "on_start_button_clicked" : self.__on_start_button_clicked, + "on_step_button_clicked" : self.__on_step_button_clicked, + "on_keep_button_clicked" : self.__on_keep_button_clicked, + "on_change_button_clicked" : self.__on_change_button_clicked, + "on_change_button_toggled" : self.__on_change_button_toggled, + "on_format_entry_keyrelease" : self.__on_format_entry_keyrelease, + "on_format_entry_focusout" : self.__on_format_entry_focusout, + "on_start_entry_focusout" : self.__on_start_entry_focusout, + "on_help_button_clicked" : self.__on_help_button_clicked, + "on_cancel_button_clicked" : self.__on_cancel_button_clicked, + "on_ok_button_clicked" : self.__on_ok_button_clicked + }) + + # Calculate all entries and update Glade window + for prim_obj, tmp in self.xobjects: + # populate Object, Actual & Quantity fields with values + obj_active = self.top.get_object('%s_active' % prim_obj) + self.active_entries[prim_obj] = MonitoredCheckbox(obj_active, obj_active, + self.obj_values[prim_obj].set_active, + self.obj_values[prim_obj].get_active) + obj_actual = self.top.get_object('%s_actual' % prim_obj) + obj_actual.set_text('%s' % self.obj_values[prim_obj].last_id()) + obj_quant = self.top.get_object('%s_quant' % prim_obj) + obj_quant.set_text('%s' % str(self.obj_values[prim_obj].quant_id)) + + # connect/populate Format, Start, Step, Keep & Change fields with GTK/values + obj_format = self.top.get_object('%s_format' % prim_obj) + self.format_entries[prim_obj] = MonitoredEntry(obj_format, + self.obj_values[prim_obj].set_fmt, + self.obj_values[prim_obj].get_fmt) + obj_change = self.top.get_object('%s_change' % prim_obj) + self.change_entries[prim_obj] = MonitoredCheckbox(obj_change, obj_change, + self.obj_values[prim_obj].set_change, + self.obj_values[prim_obj].get_change) + obj_start = self.top.get_object('%s_start' % prim_obj) + self.start_entries[prim_obj] = MonitoredEntry(obj_start, + self.obj_values[prim_obj].set_id, + self.obj_values[prim_obj].get_id) + obj_step = self.top.get_object('%s_step' % prim_obj) + self.step_entries[prim_obj] = MonitoredEntry(obj_step, + self.obj_values[prim_obj].set_step, + self.obj_values[prim_obj].get_step, + changed=self.obj_values[prim_obj].change_step) + obj_keep = self.top.get_object('%s_keep' % prim_obj) + self.keep_entries[prim_obj] = MonitoredCheckbox(obj_keep, obj_keep, + self.obj_values[prim_obj].set_keep, + self.obj_values[prim_obj].get_keep, + readonly=True) + + # fetch the popup menu + self.menu = self.top.get_object("popup_menu") + + # ok, let's see what we've done + self.show() + + def _update(self): + """ store changed objects formats in DB """ + + update = False + for prim_obj, tmp in self.xobjects: + obj_value = self.obj_values[prim_obj] + if obj_value.object_fmt != obj_value.stored_fmt: + prefix = obj_value.object_prefix.lower() + constant = 'preferences.%sprefix' % prefix + config.set(constant, obj_value.object_fmt) + update = True + + if update: + config.save() + self.dbstate.set_prefixes( + config.get('preferences.iprefix'), + config.get('preferences.oprefix'), + config.get('preferences.fprefix'), + config.get('preferences.sprefix'), + config.get('preferences.cprefix'), + config.get('preferences.pprefix'), + config.get('preferences.eprefix'), + config.get('preferences.rprefix'), + config.get('preferences.nprefix')) + + def _execute(self): + """ execute all primary objects and reorder if neccessary """ + + # Update progress calculation + if self.uistate: + self.progress = ProgressMeter(_('Reorder Gramps IDs'), '') + else: + total_objs = 0 + for prim_obj, tmp in self.xobjects: + if self.obj_values[prim_obj].active_obj: + total_objs += self.obj_values[prim_obj].quant_id + self.set_total(total_objs) + + # Update database + self.dbstate.disable_signals() + for prim_obj, prim_objs in self.xobjects: + with DbTxn(_('Reorder %s IDs ...') % prim_obj, self.dbstate, batch=True) \ + as self.trans: + if self.obj_values[prim_obj].active_obj: + if self.uistate: + self.progress.set_pass(_('Reorder %s IDs ...') % \ + _(prim_objs.title()), \ + self.obj_values[prim_obj].quant_id) + # Process reordering + self._reorder(prim_obj) + + self.dbstate.enable_signals() + self.dbstate.request_rebuild() + + # Update progress calculation + if self.uistate: + self.progress.close() + else: + print('\nDone.') + + # finds integer portion in a GrampsID + _findint = re.compile('^[^\d]*(\d+)[^\d]*') + def _reorder(self, prim_obj): + """ reorders all selected objects with a (new) style, start & step """ + + dup_ids = [] # list of duplicate identifiers + new_ids = {} # list of new identifiers + + class_type, table, commit, get_from_id, get_from_handle, next_from_id = \ + self.obj_methods[prim_obj] + + prefix_fmt = self.obj_values[prim_obj].get_fmt() + prefix = prefix_fmt.split('%', 1)[0] + new_id = self.obj_values[prim_obj].get_id() + keep_fmt = self.obj_values[prim_obj].get_keep() + change = self.obj_values[prim_obj].get_change() + + formatmatch = _parseformat.match(prefix_fmt) + index_max = int("9" * int(formatmatch.groups()[0])) + + for handle in list(table.keys()): + # Update progress if self.uistate: self.progress.step() + else: + self.update() - obj = find_from_handle(handle) + # extract basic data out of the database + table_data = table[handle] + obj = class_type() + obj.unserialize(table_data) - gramps_id = obj.get_gramps_id() - # attempt to extract integer, if we can't, treat it as a - # duplicate + act_id = obj.get_gramps_id() + if change: + # update the defined ID numbers into objects under consideration + # of keeping ID if format not matches prefix + # (implication logical boolean operator below) + if act_id.startswith(prefix) or not keep_fmt: + obj.set_gramps_id(new_id) + commit(obj, self.trans) + new_id = self.obj_values[prim_obj].succ_id() + else: + # attempt to extract integer - if we can't, treat it as a duplicate + try: + match = _findint.match(act_id) + if match: + # get the integer, build the new handle. Make sure it + # hasn't already been chosen. If it has, put this + # in the duplicate handle list - try: - match = _findint.match(gramps_id) - if match: - # get the integer, build the new handle. Make sure it - # hasn't already been chosen. If it has, put this - # in the duplicate handle list - - index = match.groups()[0] - - if formatmatch: - if int(index) > int("9" * int(formatmatch.groups()[0])): - newgramps_id = find_next_id() + index = int(match.groups()[0]) + if formatmatch: + if index > index_max: + new_id = next_from_id() + else: + new_id = prefix_fmt % index else: - newgramps_id = prefix % int(index) - else: - # the prefix does not contain a number after %, eg I%d - newgramps_id = prefix % int(index) + # prefix_fmt does not contain a number after %, eg I%d + new_id = prefix_fmt % index - if newgramps_id == gramps_id: - if newgramps_id in newids: - dups.append(obj.get_handle()) + if new_id == act_id: + if new_id in new_ids: + dup_ids.append(obj.get_handle()) + else: + new_ids[new_id] = act_id + elif get_from_id(new_id) is not None: + dup_ids.append(obj.get_handle()) else: - newids[newgramps_id] = gramps_id - elif find_from_id(newgramps_id) is not None: - dups.append(obj.get_handle()) + obj.set_id(new_id) + commit(obj, self.trans) + new_ids[new_id] = act_id else: - obj.set_gramps_id(newgramps_id) - commit(obj, self.trans) - newids[newgramps_id] = gramps_id - else: - dups.append(handle) - except: - dups.append(handle) + dup_ids.append(handle) + except: + dup_ids.append(handle) # go through the duplicates, looking for the first available # handle that matches the new scheme. - - if self.uistate: - self.progress.set_pass(_('Finding and assigning unused IDs'), - len(dups)) - for handle in dups: + if dup_ids: if self.uistate: - self.progress.step() - obj = find_from_handle(handle) - obj.set_gramps_id(find_next_id()) - commit(obj, self.trans) + self.progress.set_pass(_('Finding and assigning unused IDs.'), len(dup_ids)) + for handle in dup_ids: + obj = get_from_handle(handle) + obj.set_gramps_id(next_from_id()) + commit(obj, self.trans) #------------------------------------------------------------------------ # @@ -243,9 +633,7 @@ class ReorderIds(tool.BatchTool): # #------------------------------------------------------------------------ class ReorderIdsOptions(tool.ToolOptions): - """ - Defines options and provides handling interface. - """ + """ Defines options and provides handling interface. """ def __init__(self, name, person_id=None): tool.ToolOptions.__init__(self, name, person_id) diff --git a/po/POTFILES.in b/po/POTFILES.in index cb47b952d..8f6e01eda 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -719,6 +719,7 @@ gramps/plugins/tool/relcalc.glade gramps/plugins/tool/relcalc.py gramps/plugins/tool/removeunused.glade gramps/plugins/tool/removeunused.py +gramps/plugins/tool/reorderids.glade gramps/plugins/tool/reorderids.py gramps/plugins/tool/sortevents.py gramps/plugins/tool/testcasegenerator.py