Removed Django import/export and CalcEstimateDates (moved to addons)
svn: r14685
This commit is contained in:
		| @@ -381,7 +381,6 @@ src/plugins/drawreport/TimeLine.py | ||||
| # plugins/export directory | ||||
| src/plugins/export/export.gpr.py | ||||
| src/plugins/export/ExportCsv.py | ||||
| src/plugins/export/ExportDjango.py | ||||
| src/plugins/export/ExportFtree.py | ||||
| src/plugins/export/ExportGedcom.py | ||||
| src/plugins/export/ExportGeneWeb.py | ||||
| @@ -420,8 +419,6 @@ src/plugins/graph/GVRelGraph.py | ||||
| # plugins/import directory | ||||
| src/plugins/import/import.gpr.py | ||||
| src/plugins/import/ImportCsv.py | ||||
| src/plugins/import/ImportDjango.py | ||||
| src/plugins/import/ImportDjango.py | ||||
| src/plugins/import/ImportGedcom.py | ||||
| src/plugins/import/ImportGeneWeb.py | ||||
| src/plugins/import/ImportGrdb.py | ||||
| @@ -506,7 +503,6 @@ src/plugins/textreport/Summary.py | ||||
| src/plugins/textreport/textplugins.gpr.py | ||||
|  | ||||
| # plugins/tool directory | ||||
| src/plugins/tool/CalculateEstimatedDates.py | ||||
| src/plugins/tool/ChangeNames.py | ||||
| src/plugins/tool/ChangeTypes.py | ||||
| src/plugins/tool/Check.py | ||||
|   | ||||
| @@ -1,37 +0,0 @@ | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008 - 2009  Douglas S. Blank <doug.blank@gmail.com> | ||||
| # Copyright (C) 2009         B. Malengier <benny.malengier@gmail.com> | ||||
| # | ||||
| # 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$ | ||||
| # | ||||
|  | ||||
| register(EXPORT, | ||||
|          id                   = "export_django", | ||||
|          name                 = _('Django Export'), | ||||
|          description          = _('Django is a web framework working on a ' | ||||
|                                   'configured database'), | ||||
|          version              = '1.0', | ||||
|          gramps_target_version= '3.3', | ||||
|          status               = UNSTABLE, | ||||
|          export_options_title = _('Django options'),  | ||||
|          export_options       = 'NoFilenameOptions', | ||||
|          export_function      = 'export_all', | ||||
|          extension            = "django", | ||||
|          fname                = "ExportDjango.py", | ||||
|          ) | ||||
|  | ||||
| @@ -1,193 +0,0 @@ | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008 - 2009  Douglas S. Blank <doug.blank@gmail.com> | ||||
| # Copyright (C) 2009         B. Malengier <benny.malengier@gmail.com> | ||||
| # | ||||
| # 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$ | ||||
| # | ||||
|  | ||||
| """ | ||||
| Export to the Django Models on the configured database backend | ||||
|  | ||||
|  | ||||
| """ | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Standard Python Modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| import sys | ||||
| import os | ||||
| import time | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Set up logging | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| import logging | ||||
| LOG = logging.getLogger(".ExportDjango") | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| import ExportOptions | ||||
| from Utils import create_id, probably_alive | ||||
| import const | ||||
| import gen.lib | ||||
|  | ||||
| from TransUtils import get_addon_translator | ||||
| translator = get_addon_translator(__file__) | ||||
| _ = translator.gettext | ||||
| ngettext = translator.ngettext | ||||
|  | ||||
| from django.conf import settings | ||||
| import web.settings as default_settings | ||||
| try: | ||||
|     settings.configure(default_settings) | ||||
| except RuntimeError: | ||||
|     # already configured; ignore | ||||
|     pass | ||||
|  | ||||
| from web.libdjango import DjangoInterface | ||||
|  | ||||
| def export_all(database, filename, option_box=None, callback=None): | ||||
|     if not callable(callback):  | ||||
|         callback = lambda (percent): None # dummy | ||||
|  | ||||
|     start = time.time() | ||||
|     total = (database.get_number_of_notes() +  | ||||
|              database.get_number_of_people() + | ||||
|              database.get_number_of_events() +  | ||||
|              database.get_number_of_families() + | ||||
|              database.get_number_of_repositories() + | ||||
|              database.get_number_of_places() + | ||||
|              database.get_number_of_media_objects() + | ||||
|              database.get_number_of_sources()) * 2 # steps | ||||
|     count = 0.0 | ||||
|     dji = DjangoInterface() | ||||
|     dji.clear_tables("primary", "secondary", "ref") | ||||
|  | ||||
|     for step in [0, 1]: | ||||
|         LOG.debug("Exporting Step %d..." % (step + 1)) | ||||
|         # --------------------------------- | ||||
|         # Person | ||||
|         # --------------------------------- | ||||
|         for person_handle in database.person_map.keys(): | ||||
|             data = database.person_map[person_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_person(data) | ||||
|             elif step == 1: | ||||
|                 djperson = dji.add_person_detail(data) | ||||
|                 djperson.probably_alive = not bool(djperson.death) | ||||
|                 djperson.save() | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Notes | ||||
|         # --------------------------------- | ||||
|         for note_handle in database.note_map.keys(): | ||||
|             data = database.note_map[note_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_note(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Family | ||||
|         # --------------------------------- | ||||
|         for family_handle in database.family_map.keys(): | ||||
|             data = database.family_map[family_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_family(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_family_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Source | ||||
|         # --------------------------------- | ||||
|         for source_handle in database.source_map.keys(): | ||||
|             data = database.source_map[source_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_source(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_source_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Event | ||||
|         # --------------------------------- | ||||
|         for event_handle in database.event_map.keys(): | ||||
|             data = database.event_map[event_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_event(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_event_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Repository | ||||
|         # --------------------------------- | ||||
|         for repository_handle in database.repository_map.keys(): | ||||
|             data = database.repository_map[repository_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_repository(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_repository_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|      | ||||
|         # --------------------------------- | ||||
|         # Place  | ||||
|         # --------------------------------- | ||||
|         for place_handle in database.place_map.keys(): | ||||
|             data = database.place_map[place_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_place(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_place_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|      | ||||
|         # --------------------------------- | ||||
|         # Media | ||||
|         # --------------------------------- | ||||
|         for media_handle in database.media_map.keys(): | ||||
|             data = database.media_map[media_handle] | ||||
|             if step == 0: | ||||
|                 dji.add_media(data) | ||||
|             elif step == 1: | ||||
|                 dji.add_media_detail(data) | ||||
|             count += 1 | ||||
|             callback(100 * count/total) | ||||
|  | ||||
|     total_time = time.time() - start | ||||
|     msg = ngettext('Export Complete: %d second','Export Complete: %d seconds', total_time ) % total_time | ||||
|     LOG.debug(msg) | ||||
|     return True | ||||
|  | ||||
| class NoFilenameOptions(ExportOptions.WriterOptionBox): | ||||
|     no_fileselect = True | ||||
|  | ||||
| @@ -8,7 +8,6 @@ pkgdatadir = $(datadir)/@PACKAGE@/plugins/export | ||||
| pkgdata_PYTHON = \ | ||||
| 	export.gpr.py \ | ||||
| 	ExportCsv.py \ | ||||
| 	ExportDjango.py \ | ||||
| 	ExportFtree.py \ | ||||
| 	ExportGedcom.py \ | ||||
| 	ExportGeneWeb.py \ | ||||
|   | ||||
| @@ -1,35 +0,0 @@ | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008 - 2009  Douglas S. Blank <doug.blank@gmail.com> | ||||
| # Copyright (C) 2009         B. Malengier <benny.malengier@gmail.com> | ||||
| # | ||||
| # 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$ | ||||
| # | ||||
|  | ||||
| register(IMPORT, | ||||
|          id                   = "import_django", | ||||
|          name                 = _('Django Import'), | ||||
|          description          = _('Django is a web framework working on a ' | ||||
|                                   'configured database'), | ||||
|          version              = '1.0', | ||||
|          gramps_target_version = '3.3', | ||||
|          status               = UNSTABLE, | ||||
|          import_function      = 'import_data', | ||||
|          extension            = "django", | ||||
|          fname                = "ImportDjango.py", | ||||
|          ) | ||||
|  | ||||
| @@ -1,195 +0,0 @@ | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2008 - 2009  Douglas S. Blank <doug.blank@gmail.com> | ||||
| # Copyright (C) 2009         B. Malengier <benny.malengier@gmail.com> | ||||
| # | ||||
| # 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$ | ||||
| # | ||||
|  | ||||
| """ | ||||
| Import from the Django dji on the configured database backend | ||||
|  | ||||
| """ | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Standard Python Modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import time | ||||
| import sys | ||||
| import os | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Set up logging | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| import logging | ||||
| LOG = logging.getLogger(".ImportDjango") | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| import gen.lib | ||||
| from QuestionDialog import ErrorDialog | ||||
| from Utils import create_id | ||||
| import const | ||||
|  | ||||
| from TransUtils import get_addon_translator | ||||
| translator = get_addon_translator(__file__) | ||||
| _ = translator.gettext | ||||
| ngettext = translator.ngettext | ||||
|  | ||||
| from django.conf import settings | ||||
| import web.settings as default_settings | ||||
| try: | ||||
|     settings.configure(default_settings) | ||||
| except: | ||||
|     pass | ||||
|  | ||||
| from web.libdjango import DjangoInterface | ||||
|  | ||||
| #------------------------------------------------------------------------- | ||||
| # | ||||
| # Django Reader | ||||
| # | ||||
| #------------------------------------------------------------------------- | ||||
| class DjangoReader(object): | ||||
|     def __init__(self, db, filename, callback): | ||||
|         if not callable(callback):  | ||||
|             callback = lambda (percent): None # dummy | ||||
|         self.db = db | ||||
|         self.dji = DjangoInterface() | ||||
|         self.filename = filename | ||||
|         self.callback = callback | ||||
|         self.debug = 0 | ||||
|  | ||||
|     def process(self): | ||||
|         sql = None | ||||
|         total = (self.dji.Note.count() +  | ||||
|                  self.dji.Person.count() +  | ||||
|                  self.dji.Event.count() + | ||||
|                  self.dji.Family.count() + | ||||
|                  self.dji.Repository.count() + | ||||
|                  self.dji.Place.count() + | ||||
|                  self.dji.Media.count() + | ||||
|                  self.dji.Source.count()) | ||||
|         self.trans = self.db.transaction_begin("",batch=True) | ||||
|         self.db.disable_signals() | ||||
|         count = 0.0 | ||||
|         self.t = time.time() | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process note | ||||
|         # --------------------------------- | ||||
|         notes = self.dji.Note.all() | ||||
|         for note in notes: | ||||
|             data = self.dji.get_note(note) | ||||
|             self.db.note_map[str(note.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process event | ||||
|         # --------------------------------- | ||||
|         events = self.dji.Event.all() | ||||
|         for event in events: | ||||
|             data = self.dji.get_event(event) | ||||
|             self.db.event_map[str(event.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process person | ||||
|         # --------------------------------- | ||||
|         ## Do this after Events to get the birth/death data | ||||
|         people = self.dji.Person.all() | ||||
|         for person in people: | ||||
|             data = self.dji.get_person(person) | ||||
|             self.db.person_map[str(person.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process family | ||||
|         # --------------------------------- | ||||
|         families = self.dji.Family.all() | ||||
|         for family in families: | ||||
|             data = self.dji.get_family(family) | ||||
|             self.db.family_map[str(family.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process repository | ||||
|         # --------------------------------- | ||||
|         repositories = self.dji.Repository.all() | ||||
|         for repo in repositories: | ||||
|             data = self.dji.get_repository(repo) | ||||
|             self.db.repository_map[str(repo.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process place | ||||
|         # --------------------------------- | ||||
|         places = self.dji.Place.all() | ||||
|         for place in places: | ||||
|             data = self.dji.get_place(place) | ||||
|             self.db.place_map[str(place.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process source | ||||
|         # --------------------------------- | ||||
|         sources = self.dji.Source.all() | ||||
|         for source in sources: | ||||
|             data = self.dji.get_source(source) | ||||
|             self.db.source_map[str(source.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|         # --------------------------------- | ||||
|         # Process media | ||||
|         # --------------------------------- | ||||
|         media = self.dji.Media.all() | ||||
|         for med in media: | ||||
|             data = self.dji.get_media(med) | ||||
|             self.db.media_map[str(med.handle)] = data | ||||
|             count += 1 | ||||
|             self.callback(100 * count/total) | ||||
|  | ||||
|  | ||||
|         return None | ||||
|  | ||||
|     def cleanup(self): | ||||
|         self.t = time.time() - self.t | ||||
|         msg = ngettext('Import Complete: %d second','Import Complete: %d seconds', self.t ) % self.t | ||||
|         self.db.transaction_commit(self.trans, _("Django import")) | ||||
|         self.db.enable_signals() | ||||
|         self.db.request_rebuild() | ||||
|         LOG.debug(msg) | ||||
|  | ||||
| def import_data(db, filename, callback=None): | ||||
|     g = DjangoReader(db, filename, callback) | ||||
|     g.process() | ||||
|     g.cleanup() | ||||
|  | ||||
| @@ -8,7 +8,6 @@ pkgdatadir = $(datadir)/@PACKAGE@/plugins/import | ||||
| pkgdata_PYTHON = \ | ||||
| 	import.gpr.py \ | ||||
| 	ImportCsv.py \ | ||||
| 	ImportDjango.py \ | ||||
| 	ImportGedcom.py \ | ||||
| 	ImportGeneWeb.py \ | ||||
| 	ImportGpkg.py \ | ||||
|   | ||||
| @@ -1,543 +0,0 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # Gramps - a GTK+/GNOME based genealogy program | ||||
| # | ||||
| # Copyright (C) 2007  Donald N. Allingham | ||||
| # Copyright (C) 2008  Brian 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 | ||||
| # | ||||
|  | ||||
| # $Id$ | ||||
|  | ||||
| "Calculate Estimated Dates" | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # python modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| from gen.ggettext import gettext as _ | ||||
| import time | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # GRAMPS modules | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| from PluginUtils import Tool, PluginWindows, MenuToolOptions | ||||
| from gen.plug.menu import BooleanOption, NumberOption, StringOption, \ | ||||
|                          FilterOption, PersonOption, EnumeratedListOption | ||||
| import gen.lib | ||||
| import config | ||||
| from gen.display.name import displayer as name_displayer | ||||
| import Errors | ||||
| from ReportBase import ReportUtils | ||||
| from docgen import TextBufDoc | ||||
| from Simple import make_basic_stylesheet, SimpleAccess, SimpleDoc, SimpleTable | ||||
| from QuestionDialog import QuestionDialog | ||||
| from Utils import create_id, probably_alive_range | ||||
| import DateHandler | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Tool Classes | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
| class CalcEstDateOptions(MenuToolOptions): | ||||
|     """ Calculate Estimated Date options  """ | ||||
|     def __init__(self, name, person_id=None, dbstate=None): | ||||
|         self.__db = dbstate.get_database() | ||||
|         self.__dbstate = dbstate | ||||
|         MenuToolOptions.__init__(self, name, person_id, dbstate) | ||||
|  | ||||
|     def get_dbstate(self): | ||||
|         return self.__dbstate | ||||
|      | ||||
|     def add_menu_options(self, menu): | ||||
|          | ||||
|         """ Add the options """ | ||||
|         category_name = _("Options") | ||||
|          | ||||
|         self.__filter = FilterOption(_("Filter"), 0) | ||||
|         self.__filter.set_help(_("Select filter to restrict people")) | ||||
|         menu.add_option(category_name, "filter", self.__filter) | ||||
|         self.__filter.connect('value-changed', self.__filter_changed) | ||||
|          | ||||
|         self.__pid = PersonOption(_("Filter Person")) | ||||
|         self.__pid.set_help(_("The center person for the filter")) | ||||
|         menu.add_option(category_name, "pid", self.__pid) | ||||
|         self.__pid.connect('value-changed', self.__update_filters) | ||||
|          | ||||
|         self.__update_filters() | ||||
|  | ||||
|         source_text = StringOption(_("Source text"),  | ||||
|                                    _("Calculated Date Estimates")) | ||||
|         source_text.set_help(_("Source to remove and/or add")) | ||||
|         menu.add_option(category_name, "source_text", source_text) | ||||
|  | ||||
|         remove = BooleanOption(_("Remove previously added events, notes, and source"), True) | ||||
|         remove.set_help(_("Remove calculated events, notes, and source; occurs immediately on Execute")) | ||||
|         menu.add_option(category_name, "remove", remove) | ||||
|  | ||||
|         birth = EnumeratedListOption(_("Birth"), 0) | ||||
|         birth.add_item(0, _("Do not add birth events")) | ||||
|         birth.add_item(1, _("Add birth events without dates")) | ||||
|         birth.add_item(2, _("Add birth events with dates")) | ||||
|         birth.set_help( _("Add a birth events with or without estimated dates")) | ||||
|         menu.add_option(category_name, "add_birth", birth) | ||||
|  | ||||
|         death = EnumeratedListOption(_("Death"), 0) | ||||
|         death.add_item(0, _("Do not add death events")) | ||||
|         death.add_item(1, _("Add death events without dates")) | ||||
|         death.add_item(2, _("Add death events with dates")) | ||||
|         death.set_help( _("Add death events with or without estimated dates")) | ||||
|         menu.add_option(category_name, "add_death", death) | ||||
|  | ||||
|         # ----------------------------------------------------- | ||||
|         num = NumberOption(_("Maximum age"),  | ||||
|                            config.get('behavior.max-age-prob-alive'), | ||||
|                            0, 200) | ||||
|         num.set_help(_("Maximum age that one can live to")) | ||||
|         menu.add_option(category_name, "MAX_AGE_PROB_ALIVE", num) | ||||
|  | ||||
|         num = NumberOption(_("Maximum sibling age difference"),  | ||||
|                            config.get('behavior.max-sib-age-diff'), | ||||
|                            0, 200) | ||||
|         num.set_help(_("Maximum age difference between siblings")) | ||||
|         menu.add_option(category_name, "MAX_SIB_AGE_DIFF", num) | ||||
|  | ||||
|         num = NumberOption(_("Average years between generations"),  | ||||
|                            config.get('behavior.avg-generation-gap'), | ||||
|                            0, 200) | ||||
|         num.set_help(_("Average years between two generations")) | ||||
|         menu.add_option(category_name, "AVG_GENERATION_GAP", num) | ||||
|  | ||||
|         dates = EnumeratedListOption(_("Estimated Dates"), 0) | ||||
|         dates.add_item(0, _("Approximate (about)")) | ||||
|         dates.add_item(1, _("Extremes (after and before)")) | ||||
|         dates.set_help( _("Dates on events are either about or after/before")) | ||||
|         menu.add_option(category_name, "dates", dates) | ||||
|          | ||||
|     def __update_filters(self): | ||||
|         """ | ||||
|         Update the filter list based on the selected person | ||||
|         """ | ||||
|         gid = self.__pid.get_value() | ||||
|         person = self.__db.get_person_from_gramps_id(gid) | ||||
|         filter_list = ReportUtils.get_person_filters(person, False) | ||||
|         self.__filter.set_filters(filter_list) | ||||
|          | ||||
|     def __filter_changed(self): | ||||
|         """ | ||||
|         Handle filter change. If the filter is not specific to a person, | ||||
|         disable the person option | ||||
|         """ | ||||
|         filter_value = self.__filter.get_value() | ||||
|         if filter_value in [1, 2, 3, 4]: | ||||
|             # Filters 0, 2, 3, 4 and 5 rely on the center person | ||||
|             self.__pid.set_available(True) | ||||
|         else: | ||||
|             # The rest don't | ||||
|             self.__pid.set_available(False) | ||||
|  | ||||
|  | ||||
| class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         PluginWindows.ToolManagedWindowBatch.__init__(self, *args, **kwargs) | ||||
|         if self.fail: return | ||||
|         self.help_page = self.add_page(_("Help")) | ||||
|         self.write_to_page(self.help_page,  | ||||
|                            _("The Calculate Estimated Dates Tool is used to add and remove " | ||||
|                            "birth and death events for people that are missing these " | ||||
|                            "events.\n\n" | ||||
|                            "To use:\n" | ||||
|                            "1. Go to the Options tab\n" | ||||
|                            "2. Check the [ ] Remove option to remove previous estimates\n" | ||||
|                            "3. Select the Add date options to date events with or without dates\n" | ||||
|                            "4. Click on Execute\n" | ||||
|                            "5. Select the people with which to add events\n" | ||||
|                            "6. Click on 'Add Selected Events' button to create\n\n" | ||||
|                            "NOTES: if you decide to make an event permanent, remove it from " | ||||
|                            "the Source. Otherwise, it will get removed the next time you " | ||||
|                            "automatically remove these events.\n\n" | ||||
|                            "You may have to run the tool repeatedly (without removing previous " | ||||
|                            "events) to add all of the events possible.")) | ||||
|         self.set_current_frame(_("Help")) | ||||
|  | ||||
|     def get_title(self): | ||||
|         return _("Calculate Estimated Dates") | ||||
|  | ||||
|     def initial_frame(self): | ||||
|         return _("Options") | ||||
|  | ||||
|     def set_reselect(self): | ||||
|         self.reselect = True | ||||
|  | ||||
|     def run(self): | ||||
|         BUTTONS = ((_("Select All"), self.select_all), | ||||
|                    (_("Select None"), self.select_none), | ||||
|                    (_("Toggle Selection"), self.toggle_select), | ||||
|                    (_("Add Selected Events"), self.apply_selection), | ||||
|                    ) | ||||
|  | ||||
|         if hasattr(self, "table") and self.table: | ||||
|             self.reselect = False | ||||
|             if self.options.handler.options_dict['remove']: | ||||
|                 QuestionDialog(_("Remove Events, Notes, and Source and Reselect Data"), | ||||
|                                _("Are you sure you want to remove previous events, notes, and source and reselect data?"), | ||||
|                                _("Remove and Run Select Again"), | ||||
|                                self.set_reselect, | ||||
|                                self.window) | ||||
|             else: | ||||
|                 QuestionDialog(_("Reselect Data"), | ||||
|                                _("Are you sure you want to reselect data?"), | ||||
|                                _("Run Select Again"), | ||||
|                                self.set_reselect, | ||||
|                                self.window) | ||||
|             if not self.reselect: | ||||
|                 return | ||||
|  | ||||
|         current_date = gen.lib.Date() | ||||
|         current_date.set_yr_mon_day(*time.localtime(time.time())[0:3]) | ||||
|         self.action = {} | ||||
|         widget = self.add_results_frame(_("Select")) | ||||
|         document = TextBufDoc(make_basic_stylesheet(), None) | ||||
|         document.dbstate = self.dbstate | ||||
|         document.uistate = self.uistate | ||||
|         document.open("", container=widget) | ||||
|         self.sdb = SimpleAccess(self.db) | ||||
|         sdoc = SimpleDoc(document) | ||||
|         stab = SimpleTable(self.sdb) | ||||
|         self.table = stab | ||||
|         stab.columns(_("Select"), _("Person"), _("Action"),  | ||||
|                      _("Birth Date"), _("Death Date"),  | ||||
|                      _("Evidence"), _("Relative")) | ||||
|         self.results_write(_("Processing...\n")) | ||||
|         self.filter_option =  self.options.menu.get_option_by_name('filter') | ||||
|         self.filter = self.filter_option.get_filter() # the actual filter | ||||
|         people = self.filter.apply(self.db, | ||||
|                                    self.db.iter_person_handles()) | ||||
|         num_people = self.db.get_number_of_people() | ||||
|         source_text = self.options.handler.options_dict['source_text'] | ||||
|         source = None | ||||
|         add_birth = self.options.handler.options_dict['add_birth'] | ||||
|         add_death = self.options.handler.options_dict['add_death'] | ||||
|         remove_old = self.options.handler.options_dict['remove'] | ||||
|  | ||||
|         self.MAX_SIB_AGE_DIFF = self.options.handler.options_dict['MAX_SIB_AGE_DIFF'] | ||||
|         self.MAX_AGE_PROB_ALIVE = self.options.handler.options_dict['MAX_AGE_PROB_ALIVE'] | ||||
|         self.AVG_GENERATION_GAP = self.options.handler.options_dict['AVG_GENERATION_GAP'] | ||||
|         if remove_old: | ||||
|             self.trans = self.db.transaction_begin("",batch=True) | ||||
|             self.db.disable_signals() | ||||
|             self.results_write(_("Removing old estimations... ")) | ||||
|             self.progress.set_pass((_("Removing '%s'...") % source_text),  | ||||
|                                    num_people) | ||||
|             for person_handle in people: | ||||
|                 self.progress.step() | ||||
|                 pupdate = 0 | ||||
|                 person = self.db.get_person_from_handle(person_handle) | ||||
|                 birth_ref = person.get_birth_ref() | ||||
|                 if birth_ref: | ||||
|                     birth = self.db.get_event_from_handle(birth_ref.ref) | ||||
|                     source_list = birth.get_source_references() | ||||
|                     for source_ref in source_list: | ||||
|                         #print "birth handle:", source_ref | ||||
|                         source = self.db.get_source_from_handle(source_ref.ref) | ||||
|                         if source: | ||||
|                             #print "birth source:", source, source.get_title() | ||||
|                             if source.get_title() == source_text: | ||||
|                                 person.set_birth_ref(None) | ||||
|                                 person.remove_handle_references('Event',[birth_ref.ref]) | ||||
|                                 # remove note | ||||
|                                 note_list = birth.get_referenced_note_handles() | ||||
|                                 birth.remove_handle_references('Note',  | ||||
|                                   [note_handle for (obj_type, note_handle) in note_list]) | ||||
|                                 for (obj_type, note_handle) in note_list: | ||||
|                                     self.db.remove_note(note_handle, self.trans) | ||||
|                                 self.db.remove_event(birth_ref.ref, self.trans) | ||||
|                                 self.db.commit_source(source, self.trans) | ||||
|                                 pupdate = 1 | ||||
|                                 break | ||||
|                 death_ref = person.get_death_ref() | ||||
|                 if death_ref: | ||||
|                     death = self.db.get_event_from_handle(death_ref.ref) | ||||
|                     source_list = death.get_source_references() | ||||
|                     for source_ref in source_list: | ||||
|                         #print "death handle:", source_ref | ||||
|                         source = self.db.get_source_from_handle(source_ref.ref) | ||||
|                         if source: | ||||
|                             #print "death source:", source, source.get_title() | ||||
|                             if source.get_title() == source_text: | ||||
|                                 person.set_death_ref(None) | ||||
|                                 person.remove_handle_references('Event',[death_ref.ref]) | ||||
|                                 # remove note | ||||
|                                 note_list = death.get_referenced_note_handles() | ||||
|                                 birth.remove_handle_references('Note',  | ||||
|                                   [note_handle for (obj_type, note_handle) in note_list]) | ||||
|                                 for (obj_type, note_handle) in note_list: | ||||
|                                     self.db.remove_note(note_handle, self.trans) | ||||
|                                 self.db.remove_event(death_ref.ref, self.trans) | ||||
|                                 self.db.commit_source(source, self.trans) | ||||
|                                 pupdate = 1 | ||||
|                                 break | ||||
|                 if pupdate == 1: | ||||
|                     self.db.commit_person(person, self.trans) | ||||
|             if source: | ||||
|                 self.db.remove_source(source.handle, self.trans) | ||||
|             self.results_write(_("done!\n")) | ||||
|             self.db.transaction_commit(self.trans, _("Removed date estimates")) | ||||
|             self.db.enable_signals() | ||||
|             self.db.request_rebuild() | ||||
|         if add_birth or add_death: | ||||
|             self.results_write(_("Selecting... \n\n")) | ||||
|             self.progress.set_pass(_('Selecting...'),  | ||||
|                                    num_people) | ||||
|             row = 0 | ||||
|             for person_handle in people: | ||||
|                 self.progress.step() | ||||
|                 person = self.db.get_person_from_handle(person_handle) | ||||
|                 birth_ref = person.get_birth_ref() | ||||
|                 death_ref = person.get_death_ref() | ||||
|                 add_birth_event, add_death_event = False, False | ||||
|                 if not birth_ref or not death_ref: | ||||
|                     date1, date2, explain, other = self.calc_estimates(person) | ||||
|                     if birth_ref: | ||||
|                         ev = self.db.get_event_from_handle(birth_ref.ref) | ||||
|                         date1 = ev.get_date_object() | ||||
|                     elif not birth_ref and add_birth and date1: | ||||
|                         if date1.match( current_date, "<"): | ||||
|                             add_birth_event = True | ||||
|                             date1.make_vague() | ||||
|                         else: | ||||
|                             date1 = gen.lib.Date() | ||||
|                     else: | ||||
|                         date1 = gen.lib.Date() | ||||
|                     if death_ref: | ||||
|                         ev = self.db.get_event_from_handle(death_ref.ref) | ||||
|                         date2 = ev.get_date_object() | ||||
|                     elif not death_ref and add_death and date2: | ||||
|                         if date2.match( current_date, "<"): | ||||
|                             add_death_event = True | ||||
|                             date2.make_vague() | ||||
|                         else: | ||||
|                             date2 = gen.lib.Date() | ||||
|                     else: | ||||
|                         date2 = gen.lib.Date() | ||||
|                     # Describe | ||||
|                     if add_birth_event and add_death_event:  | ||||
|                         action = _("Add birth and death events") | ||||
|                     elif add_birth_event: | ||||
|                         action = _("Add birth event") | ||||
|                     elif add_death_event:  | ||||
|                         action = _("Add death event") | ||||
|                     else: | ||||
|                         continue | ||||
|                     #stab.columns(_("Select"), _("Person"), _("Action"),  | ||||
|                     # _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) | ||||
|                     if add_birth == 1 and not birth_ref: # no date | ||||
|                         date1 = gen.lib.Date() | ||||
|                     if add_death == 1 and not death_ref: # no date | ||||
|                         date2 = gen.lib.Date() | ||||
|                     if person == other: | ||||
|                         other = None | ||||
|                     stab.row("checkbox",  | ||||
|                              person,  | ||||
|                              action,  | ||||
|                              date1, | ||||
|                              date2, | ||||
|                              explain or "",  | ||||
|                              other or "") | ||||
|                     if add_birth_event: | ||||
|                         stab.set_cell_markup(3, row, "<b>%s</b>" % DateHandler.displayer.display(date1)) | ||||
|                     if add_death_event: | ||||
|                         stab.set_cell_markup(4, row, "<b>%s</b>" % DateHandler.displayer.display(date2)) | ||||
|                     self.action[person.handle] = (add_birth_event, add_death_event) | ||||
|                     row += 1 | ||||
|             if row > 0: | ||||
|                 self.results_write("  ") | ||||
|                 for text, function in BUTTONS: | ||||
|                     self.make_button(text, function, widget) | ||||
|                 self.results_write("\n") | ||||
|                 stab.write(sdoc) | ||||
|                 self.results_write("  ") | ||||
|                 for text, function in BUTTONS: | ||||
|                     self.make_button(text, function, widget) | ||||
|                 self.results_write("\n") | ||||
|             else: | ||||
|                 self.results_write(_("No events to be added.")) | ||||
|                 self.results_write("\n") | ||||
|         self.results_write("\n") | ||||
|         self.progress.close() | ||||
|         self.set_current_frame(_("Select")) | ||||
|  | ||||
|     def make_button(self, text, function, widget): | ||||
|         import gtk | ||||
|         button = gtk.Button(text) | ||||
|         buffer = widget.get_buffer() | ||||
|         iter = buffer.get_end_iter() | ||||
|         anchor = buffer.create_child_anchor(iter) | ||||
|         widget.add_child_at_anchor(button, anchor) | ||||
|         button.connect("clicked", function) | ||||
|         button.show() | ||||
|         self.results_write("  ") | ||||
|  | ||||
|     def select_all(self, obj): | ||||
|         select_col = self.table.model_index_of_column[_("Select")] | ||||
|         for row in self.table.treeview.get_model(): | ||||
|             row[select_col] = True  | ||||
|  | ||||
|     def select_none(self, obj): | ||||
|         select_col = self.table.model_index_of_column[_("Select")] | ||||
|         for row in self.table.treeview.get_model(): | ||||
|             row[select_col] = False | ||||
|  | ||||
|     def toggle_select(self, obj): | ||||
|         select_col = self.table.model_index_of_column[_("Select")] | ||||
|         for row in self.table.treeview.get_model(): | ||||
|             row[select_col] = not row[select_col] | ||||
|  | ||||
|     def apply_selection(self, *args, **kwargs): | ||||
|         # Do not add birth or death event if one exists, no matter what | ||||
|         if self.table.treeview.get_model() is None: | ||||
|             return | ||||
|         self.trans = self.db.transaction_begin("",batch=True) | ||||
|         self.pre_run() | ||||
|         source_text = self.options.handler.options_dict['source_text'] | ||||
|         select_col = self.table.model_index_of_column[_("Select")] | ||||
|         source = self.get_or_create_source(source_text) | ||||
|         self.db.disable_signals() | ||||
|         self.results_write(_("Selecting... ")) | ||||
|         self.progress.set_pass((_("Adding events '%s'...") % source_text),  | ||||
|                                len(self.table.treeview.get_model())) | ||||
|         count = 0 | ||||
|         for row in self.table.treeview.get_model(): | ||||
|             self.progress.step() | ||||
|             select = row[select_col] # live select value | ||||
|             if not select: | ||||
|                 continue | ||||
|             pupdate = False | ||||
|             index = row[0] # order put in | ||||
|             row_data = self.table.get_raw_data(index) | ||||
|             person = row_data[1] # check, person, action, date1, date2 | ||||
|             date1 = row_data[3] # date | ||||
|             date2 = row_data[4] # date | ||||
|             evidence = row_data[5] # evidence | ||||
|             other = row_data[6] # other person | ||||
|             add_birth_event, add_death_event = self.action[person.handle] | ||||
|             birth_ref = person.get_birth_ref() | ||||
|             death_ref = person.get_death_ref() | ||||
|             if not birth_ref and add_birth_event: | ||||
|                 other_name = self.sdb.name(other) | ||||
|                 if other_name: | ||||
|                     explanation = _("Added birth event based on %(evidence)s, from %(name)s") % { | ||||
|                         'evidence' : evidence, 'name' : other_name } | ||||
|                 else: | ||||
|                     explanation = _("Added birth event based on %s") % evidence | ||||
|                 modifier = self.get_modifier("birth") | ||||
|                 birth = self.create_event(_("Estimated birth date"),  | ||||
|                                           gen.lib.EventType.BIRTH,  | ||||
|                                           date1, source, explanation, modifier) | ||||
|                 event_ref = gen.lib.EventRef() | ||||
|                 event_ref.set_reference_handle(birth.get_handle()) | ||||
|                 person.set_birth_ref(event_ref) | ||||
|                 pupdate = True | ||||
|                 count += 1 | ||||
|             if not death_ref and add_death_event: | ||||
|                 other_name = self.sdb.name(other) | ||||
|                 if other_name: | ||||
|                     explanation = _("Added death event based on %(evidence)s, from %(person)s") % { | ||||
|                     'evidence' : evidence, 'person' : other_name } | ||||
|                 else: | ||||
|                     explanation = _("Added death event based on %s") % evidence | ||||
|                 modifier = self.get_modifier("death") | ||||
|                 death = self.create_event(_("Estimated death date"),  | ||||
|                                           gen.lib.EventType.DEATH,  | ||||
|                                           date2, source, explanation, modifier) | ||||
|                 event_ref = gen.lib.EventRef() | ||||
|                 event_ref.set_reference_handle(death.get_handle()) | ||||
|                 person.set_death_ref(event_ref) | ||||
|                 pupdate = True | ||||
|                 count += 1 | ||||
|             if pupdate: | ||||
|                 self.db.commit_person(person, self.trans) | ||||
|         self.results_write(_(" Done! Committing...")) | ||||
|         self.results_write("\n") | ||||
|         self.db.transaction_commit(self.trans, _("Add date estimates")) | ||||
|         self.db.enable_signals() | ||||
|         self.db.request_rebuild() | ||||
|         self.results_write(_("Added %d events.") % count) | ||||
|         self.results_write("\n\n") | ||||
|         self.progress.close() | ||||
|  | ||||
|     def get_modifier(self, event_type): | ||||
|         setting = self.options.handler.options_dict['dates'] | ||||
|         if event_type == "birth": | ||||
|             if setting == 0: | ||||
|                 return gen.lib.Date.MOD_ABOUT | ||||
|             else: | ||||
|                 return gen.lib.Date.MOD_AFTER | ||||
|         else: | ||||
|             if setting == 0: | ||||
|                 return gen.lib.Date.MOD_ABOUT | ||||
|             else: | ||||
|                 return gen.lib.Date.MOD_BEFORE | ||||
|  | ||||
|     def get_or_create_source(self, source_text): | ||||
|         source_list = self.db.get_source_handles() | ||||
|         for source_handle in source_list: | ||||
|             source = self.db.get_source_from_handle(source_handle) | ||||
|             if source.get_title() == source_text: | ||||
|                 return source | ||||
|         source = gen.lib.Source() | ||||
|         source.set_title(source_text) | ||||
|         self.db.add_source(source, self.trans) | ||||
|         return source | ||||
|  | ||||
|     def create_event(self, description=_("Estimated date"),  | ||||
|                      type=None, date=None, source=None,  | ||||
|                      note_text="", modifier=None): | ||||
|         event = gen.lib.Event() | ||||
|         event.set_description(description) | ||||
|         note = gen.lib.Note() | ||||
|         note.handle = create_id() | ||||
|         note.type.set(gen.lib.NoteType.EVENT) | ||||
|         note.set(note_text) | ||||
|         self.db.add_note(note, self.trans) | ||||
|         event.add_note(note.handle) | ||||
|         if type: | ||||
|             event.set_type(gen.lib.EventType(type)) | ||||
|         if date: | ||||
|             if modifier: | ||||
|                 date.set_modifier(modifier) | ||||
|             date.set_quality(gen.lib.Date.QUAL_ESTIMATED) | ||||
|             date.set_yr_mon_day(date.get_year(), 0, 0) | ||||
|             event.set_date_object(date) | ||||
|         if source: | ||||
|             sref = gen.lib.SourceRef() | ||||
|             sref.set_reference_handle(source.get_handle()) | ||||
|             event.add_source_reference(sref) | ||||
|             self.db.commit_source(source, self.trans) | ||||
|         self.db.add_event(event, self.trans) | ||||
|         return event | ||||
|  | ||||
|     def calc_estimates(self, person): | ||||
|         return probably_alive_range(person, self.db, | ||||
|                          self.MAX_SIB_AGE_DIFF,  | ||||
|                          self.MAX_AGE_PROB_ALIVE,  | ||||
|                          self.AVG_GENERATION_GAP) | ||||
|  | ||||
| @@ -6,7 +6,6 @@ | ||||
| pkgdatadir = $(datadir)/@PACKAGE@/plugins/tool | ||||
|  | ||||
| pkgdata_PYTHON = \ | ||||
| 	CalculateEstimatedDates.py \ | ||||
| 	ChangeNames.py \ | ||||
| 	ChangeTypes.py \ | ||||
| 	Check.py \ | ||||
| @@ -55,7 +54,6 @@ GLADEFILES = \ | ||||
| 	soundgen.glade \ | ||||
| 	verify.glade | ||||
|  | ||||
| 	 | ||||
| dist_pkgdata_DATA = $(GLADEFILES) | ||||
|  | ||||
| # Clean up all the byte-compiled files | ||||
|   | ||||
| @@ -24,28 +24,6 @@ | ||||
| GRAMPS registration file | ||||
| """ | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Calculate Estimated Dates | ||||
| # | ||||
| #------------------------------------------------------------------------ | ||||
|  | ||||
| register(TOOL,  | ||||
| id    = 'calculateestimateddates', | ||||
| name  = _("Calculate Estimated Dates"), | ||||
| description =  _("Calculates estimated dates for birth and death."), | ||||
| version = '0.90', | ||||
| gramps_target_version = '3.3', | ||||
| status = UNSTABLE, | ||||
| fname = 'CalculateEstimatedDates.py', | ||||
| authors = ["Douglas S. Blank"], | ||||
| authors_email = ["dblank@cs.brynmawr.edu"], | ||||
| category = TOOL_DBPROC, | ||||
| toolclass = 'CalcToolManagedWindow', | ||||
| optionclass = 'CalcEstDateOptions', | ||||
| tool_modes = [TOOL_MODE_GUI] | ||||
|   ) | ||||
|  | ||||
| #------------------------------------------------------------------------ | ||||
| # | ||||
| # Fix Capitalization of Family Names | ||||
|   | ||||
		Reference in New Issue
	
	Block a user