From 771ae761e28d166b5c8d93eaa0f28cf65602b4b4 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Mon, 31 Dec 2012 20:07:36 +0000 Subject: [PATCH] Added tagging on import for GEDCOM, XML, and CSV. Also fixed CSV iterator issue with next. Default tag is set in Preferences using time.strftime codes (eg, %Y, %m, etc). Noticed that adding source on input is only for GEDCOM, so adjusted the text description for it. Tagging on input supports all taggable items (notes, media, family, person --- although csv does not import media). Will add to gramps40 if all ok. svn: r20914 --- gramps/gen/config.py | 2 + gramps/gui/configure.py | 71 ++++++++++++++++++------- gramps/plugins/importer/importcsv.py | 30 +++++++++-- gramps/plugins/importer/importgedcom.py | 6 ++- gramps/plugins/importer/importgpkg.py | 1 + gramps/plugins/importer/importxml.py | 29 +++++++++- gramps/plugins/lib/libgedcom.py | 35 +++++++++++- 7 files changed, 147 insertions(+), 27 deletions(-) diff --git a/gramps/gen/config.py b/gramps/gen/config.py index 400ec94be..f12f9e0d8 100644 --- a/gramps/gen/config.py +++ b/gramps/gen/config.py @@ -279,6 +279,8 @@ register('preferences.date-format', 0) register('preferences.calendar-format-report', 0) register('preferences.cprefix', 'C%04d') register('preferences.default-source', False) +register('preferences.tag-on-import', False) +register('preferences.tag-on-import-format', _("Imported %Y/%m/%d %H:%M:%S")) register('preferences.eprefix', 'E%04d') register('preferences.family-warn', True) register('preferences.fprefix', 'F%04d') diff --git a/gramps/gui/configure.py b/gramps/gui/configure.py index ab3008640..406fe4e2c 100644 --- a/gramps/gui/configure.py +++ b/gramps/gui/configure.py @@ -327,13 +327,18 @@ class ConfigureDialog(ManagedWindow): config = self.__config if not callback: callback = self.update_entry - lwidget = BasicLabel("%s: " % label) + if label: + lwidget = BasicLabel("%s: " % label) entry = Gtk.Entry() entry.set_text(config.get(constant)) entry.connect('changed', callback, constant) - table.attach(lwidget, col_attach, col_attach+1, index, index+1, yoptions=0, - xoptions=Gtk.AttachOptions.FILL) - table.attach(entry, col_attach+1, col_attach+2, index, index+1, yoptions=0) + if label: + table.attach(lwidget, col_attach, col_attach+1, index, index+1, yoptions=0, + xoptions=Gtk.AttachOptions.FILL) + table.attach(entry, col_attach+1, col_attach+2, index, index+1, yoptions=0) + else: + table.attach(entry, col_attach, col_attach+1, index, index+1, yoptions=0) + return entry def add_pos_int_entry(self, table, label, index, constant, callback=None, config=None, col_attach=1): @@ -1101,6 +1106,11 @@ class GrampsPreferences(ConfigureDialog): config.set('behavior.do-not-show-previously-seen-updates', bool(active)) + def toggle_tag_on_import(self, obj): + active = obj.get_active() + config.set('preferences.tag-on-import', bool(active)) + self.tag_format_entry.set_sensitive(bool(active)) + def check_for_updates_changed(self, obj): active = obj.get_active() config.set('behavior.check-for-updates', active) @@ -1153,27 +1163,49 @@ class GrampsPreferences(ConfigureDialog): table.set_col_spacings(6) table.set_row_spacings(6) + current_line = 0 self.add_checkbox(table, - _('Add default source on import'), - 0, 'preferences.default-source') + _('Add default source on GEDCOM import'), + current_line, 'preferences.default-source') + + current_line += 1 + checkbutton = Gtk.CheckButton(_("Add tag on import")) + checkbutton.set_active(config.get('preferences.tag-on-import')) + checkbutton.connect("toggled", self.toggle_tag_on_import) + table.attach(checkbutton, 1, 2, current_line, current_line+1, yoptions=0) + self.tag_format_entry = self.add_entry(table, None, current_line, + 'preferences.tag-on-import-format', + col_attach=2) + self.tag_format_entry.set_sensitive(config.get('preferences.tag-on-import')) + + current_line += 1 self.add_checkbox(table, _('Enable spelling checker'), - 1, 'behavior.spellcheck') + current_line, 'behavior.spellcheck') + + current_line += 1 self.add_checkbox(table, _('Display Tip of the Day'), - 2, 'behavior.use-tips') + current_line, 'behavior.use-tips') + + current_line += 1 self.add_checkbox(table, _('Remember last view displayed'), - 3, 'preferences.use-last-view') + current_line, 'preferences.use-last-view') + + current_line += 1 self.add_spinner(table, _('Max generations for relationships'), - 4, 'behavior.generation-depth', (5, 50), self.update_gendepth) + current_line, 'behavior.generation-depth', (5, 50), self.update_gendepth) + + current_line += 1 self.path_entry = Gtk.Entry() self.add_path_box(table, _('Base path for relative media paths'), - 5, self.path_entry, self.dbstate.db.get_mediapath(), + current_line, self.path_entry, self.dbstate.db.get_mediapath(), self.set_mediapath, self.select_mediapath) + current_line += 1 # Check for updates: obox = Gtk.ComboBoxText() formats = [_("Never"), @@ -1186,9 +1218,10 @@ class GrampsPreferences(ConfigureDialog): obox.set_active(active) obox.connect('changed', self.check_for_updates_changed) lwidget = BasicLabel("%s: " % _('Check for updates')) - table.attach(lwidget, 1, 2, 6, 7, yoptions=0) - table.attach(obox, 2, 3, 6, 7, yoptions=0) + table.attach(lwidget, 1, 2, current_line, current_line+1, yoptions=0) + table.attach(obox, 2, 3, current_line, current_line+1, yoptions=0) + current_line += 1 self.whattype_box = Gtk.ComboBoxText() formats = [_("Updated addons only"), _("New addons only"), @@ -1203,21 +1236,23 @@ class GrampsPreferences(ConfigureDialog): self.whattype_box.set_active(0) self.whattype_box.connect('changed', self.check_for_type_changed) lwidget = BasicLabel("%s: " % _('What to check')) - table.attach(lwidget, 1, 2, 7, 8, yoptions=0) - table.attach(self.whattype_box, 2, 3, 7, 8, yoptions=0) + table.attach(lwidget, 1, 2, current_line, current_line+1, yoptions=0) + table.attach(self.whattype_box, 2, 3, current_line, current_line+1, yoptions=0) - self.add_entry(table, _('Where to check'), 8, 'behavior.addons-url', col_attach=1) + current_line += 1 + self.add_entry(table, _('Where to check'), current_line, 'behavior.addons-url', col_attach=1) + current_line += 1 checkbutton = Gtk.CheckButton( _("Do not ask about previously notified addons")) checkbutton.set_active(config.get('behavior.do-not-show-previously-seen-updates')) checkbutton.connect("toggled", self.toggle_hide_previous_addons) - table.attach(checkbutton, 0, 3, 9, 10, yoptions=0) + table.attach(checkbutton, 0, 3, current_line, current_line+1, yoptions=0) button = Gtk.Button(_("Check now")) button.connect("clicked", lambda obj: \ self.uistate.viewmanager.check_for_updates(force=True)) - table.attach(button, 3, 4, 9, 10, yoptions=0) + table.attach(button, 3, 4, current_line, current_line+1, yoptions=0) return _('General'), table diff --git a/gramps/plugins/importer/importcsv.py b/gramps/plugins/importer/importcsv.py index b3b2b6dee..8f55af3d6 100644 --- a/gramps/plugins/importer/importcsv.py +++ b/gramps/plugins/importer/importcsv.py @@ -50,7 +50,7 @@ LOG = logging.getLogger(".ImportCSV") #------------------------------------------------------------------------- from gramps.gen.ggettext import sgettext as _ from gramps.gen.ggettext import ngettext -from gramps.gen.lib import ChildRef, Citation, Event, EventRef, EventType, Family, FamilyRelType, Name, NameType, Note, NoteType, Person, Place, Source, Surname +from gramps.gen.lib import ChildRef, Citation, Event, EventRef, EventType, Family, FamilyRelType, Name, NameType, Note, NoteType, Person, Place, Source, Surname, Tag from gramps.gen.db import DbTxn from gramps.gen.plug.utils import OpenFileOrStdin from gramps.gen.datehandler import parser as _dp @@ -59,6 +59,7 @@ from gramps.gen.utils.id import create_id from gramps.gui.utils import ProgressMeter from gramps.gen.lib.eventroletype import EventRoleType from gramps.gen.constfunc import cuni, conv_to_unicode, STRTYPE +from gramps.gen.config import config #------------------------------------------------------------------------- # @@ -95,6 +96,8 @@ class UTF8Recoder(object): "Encode the next line of the file." return self.reader.next().encode("utf-8") + next = __next__ + class UnicodeReader(object): """ A CSV reader which will iterate over lines in the CSV file, @@ -120,6 +123,8 @@ class UnicodeReader(object): def __iter__(self): return self + next = __next__ + #------------------------------------------------------------------------- # # Support and main functions @@ -141,7 +146,8 @@ def rd(line_number, row, col, key, default = None): def importData(dbase, filename, user): """Function called by Gramps to import data on persons in CSV format.""" - parser = CSVParser(dbase, user) + parser = CSVParser(dbase, user, (config.get('preferences.tag-on-import-format') if + config.get('preferences.tag-on-import') else None)) try: with OpenFileOrStdin(filename, 'b') as filehandle: parser.parse(filehandle) @@ -157,7 +163,7 @@ def importData(dbase, filename, user): #------------------------------------------------------------------------- class CSVParser(object): """Class to read data in CSV format from a file object.""" - def __init__(self, dbase, user): + def __init__(self, dbase, user, default_tag_format=None): self.db = dbase self.user = user self.trans = None @@ -235,6 +241,16 @@ class CSVParser(object): for val in column2label[key]: lab2col_dict.append((val, key)) self.label2column = dict(lab2col_dict) + if default_tag_format: + name = time.strftime(default_tag_format) + tag = self.db.get_tag_from_name(name) + if tag: + self.default_tag = tag + else: + self.default_tag = Tag() + self.default_tag.set_name(name) + else: + self.default_tag = None def cleanup_column_name(self, column): """Handle column aliases for CSV spreadsheet import and SQL.""" @@ -313,6 +329,8 @@ class CSVParser(object): tym = time.time() self.db.disable_signals() with DbTxn(_("CSV import"), self.db, batch=True) as self.trans: + if self.default_tag and self.default_tag.handle is None: + self.db.add_tag(self.default_tag, self.trans) self._parse_csv_data(data, progress) self.db.enable_signals() self.db.request_rebuild() @@ -432,6 +450,8 @@ class CSVParser(object): new_note.handle = create_id() new_note.type.set(NoteType.EVENT) new_note.set(note) + if self.default_tag: + new_note.add_tag(self.default_tag.handle) self.db.add_note(new_note, self.trans) marriage.add_note(new_note.handle) self.db.commit_event(marriage, self.trans) @@ -709,6 +729,8 @@ class CSVParser(object): if wife.get_handle() != fam_wife_handle: # this wife is not the same old one! Add her! family.set_mother_handle(wife.get_handle()) + if self.default_tag: + family.add_tag(self.default_tag.handle) LOG.debug(" returning existing family") return family # if not, create one: @@ -775,6 +797,8 @@ class CSVParser(object): def create_person(self): """ Used to create a new person we know doesn't exist """ person = Person() + if self.default_tag: + person.add_tag(self.default_tag.handle) self.db.add_person(person, self.trans) self.indi_count += 1 return person diff --git a/gramps/plugins/importer/importgedcom.py b/gramps/plugins/importer/importgedcom.py index 7e2c9e8e4..cdcccd886 100644 --- a/gramps/plugins/importer/importgedcom.py +++ b/gramps/plugins/importer/importgedcom.py @@ -115,8 +115,10 @@ def importData(database, filename, user): stage_one.set_encoding(code_set) ifile.seek(0) gedparse = libgedcom.GedcomParser( - database, ifile, filename, user, stage_one, - config.get('preferences.default-source')) + database, ifile, filename, user, stage_one, + config.get('preferences.default-source'), + (config.get('preferences.tag-on-import-format') if + config.get('preferences.tag-on-import') else None)) except IOError as msg: user.notify_error(_("%s could not be opened\n") % filename, str(msg)) return diff --git a/gramps/plugins/importer/importgpkg.py b/gramps/plugins/importer/importgpkg.py index e8c6c88f0..7f6266530 100644 --- a/gramps/plugins/importer/importgpkg.py +++ b/gramps/plugins/importer/importgpkg.py @@ -94,6 +94,7 @@ def impData(database, name, user): importer = importData info = importer(database, imp_db_name, user) + newmediapath = database.get_mediapath() #import of gpkg should not change media path as all media has new paths! if not oldmediapath == newmediapath : diff --git a/gramps/plugins/importer/importxml.py b/gramps/plugins/importer/importxml.py index 904625d7c..fdaeaff36 100644 --- a/gramps/plugins/importer/importxml.py +++ b/gramps/plugins/importer/importxml.py @@ -67,6 +67,7 @@ from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, CITATION_KEY) from gramps.gen.updatecallback import UpdateCallback from gramps.gen.const import VERSION +from gramps.gen.config import config #import gramps.plugins.lib.libgrampsxml from gramps.plugins.lib import libgrampsxml @@ -123,7 +124,9 @@ def importData(database, filename, user): change = time.time() else: change = os.path.getmtime(filename) - parser = GrampsParser(database, user, change) + parser = GrampsParser(database, user, change, + (config.get('preferences.tag-on-import-format') if + config.get('preferences.tag-on-import') else None)) if filename != '-': linecounter = LineParser(filename) @@ -457,7 +460,7 @@ class ImportOpenFileContextManager: #------------------------------------------------------------------------- class GrampsParser(UpdateCallback): - def __init__(self, database, user, change): + def __init__(self, database, user, change, default_tag_format=None): UpdateCallback.__init__(self, user.callback) self.user = user self.__gramps_version = 'unknown' @@ -564,6 +567,17 @@ class GrampsParser(UpdateCallback): self.eidswap = {} self.import_handles = {} + if default_tag_format: + name = time.strftime(default_tag_format) + tag = self.db.get_tag_from_name(name) + if tag: + self.default_tag = tag + else: + self.default_tag = Tag() + self.default_tag.set_name(name) + else: + self.default_tag = None + self.func_map = { #name part "name": (self.start_name, self.stop_name), @@ -895,6 +909,9 @@ class GrampsParser(UpdateCallback): self.db.disable_signals() + if self.default_tag and self.default_tag.handle is None: + self.db.add_tag(self.default_tag, self.trans) + self.p = ParserCreate() self.p.StartElementHandler = self.startElement self.p.EndElementHandler = self.endElement @@ -1384,6 +1401,8 @@ class GrampsParser(UpdateCallback): self.person.change = int(attrs.get('change', self.change)) self.info.add('new-object', PERSON_KEY, self.person) self.convert_marker(attrs, self.person) + if self.default_tag: + self.person.add_tag(self.default_tag.handle) return self.person def start_people(self, attrs): @@ -1520,6 +1539,8 @@ class GrampsParser(UpdateCallback): if 'type' in attrs: self.family.type.set_from_xml_str(attrs["type"]) self.convert_marker(attrs, self.family) + if self.default_tag: + self.family.add_tag(self.default_tag.handle) return self.family def start_rel(self, attrs): @@ -1833,6 +1854,8 @@ class GrampsParser(UpdateCallback): #set correct change time self.db.commit_note(self.note, self.trans, self.change) self.info.add('new-object', NOTE_KEY, self.note) + if self.default_tag: + self.note.add_tag(self.default_tag.handle) return self.note def start_noteref(self, attrs): @@ -2088,6 +2111,8 @@ class GrampsParser(UpdateCallback): src = attrs.get("src", '') if src: self.object.path = src + if self.default_tag: + self.object.add_tag(self.default_tag.handle) return self.object def start_repo(self, attrs): diff --git a/gramps/plugins/lib/libgedcom.py b/gramps/plugins/lib/libgedcom.py index 62c4ce9f8..9fe68dfa4 100644 --- a/gramps/plugins/lib/libgedcom.py +++ b/gramps/plugins/lib/libgedcom.py @@ -127,7 +127,7 @@ from gramps.gen.lib import (Address, Attribute, AttributeType, ChildRef, ChildRefType, Citation, Date, Event, EventRef, EventRoleType, EventType, Family, FamilyRelType, LdsOrd, Location, MediaObject, MediaRef, Name, NameType, Note, NoteType, Person, PersonRef, Place, - RepoRef, Repository, RepositoryType, Researcher, Source, + RepoRef, Repository, RepositoryType, Researcher, Source, Tag, SourceMediaType, Surname, Url, UrlType) from gramps.gen.db import DbTxn from gramps.gen.updatecallback import UpdateCallback @@ -1807,7 +1807,7 @@ class GedcomParser(UpdateCallback): return name def __init__(self, dbase, ifile, filename, user, stage_one, - default_source): + default_source, default_tag_format=None): UpdateCallback.__init__(self, user.callback) self.user = user self.set_total(stage_one.get_line_count()) @@ -1835,6 +1835,16 @@ class GedcomParser(UpdateCallback): self.def_src = Source() fname = os.path.basename(filename).split('\\')[-1] self.def_src.set_title(_("Import from GEDCOM (%s)") % fname) + if default_tag_format: + name = time.strftime(default_tag_format) + tag = self.dbase.get_tag_from_name(name) + if tag: + self.default_tag = tag + else: + self.default_tag = Tag() + self.default_tag.set_name(name) + else: + self.default_tag = None self.dir_path = os.path.dirname(filename) self.is_ftw = False self.groups = None @@ -2594,6 +2604,8 @@ class GedcomParser(UpdateCallback): self.want_parse_warnings = True if self.use_def_src: self.dbase.add_source(self.def_src, self.trans) + if self.default_tag and self.default_tag.handle is None: + self.dbase.add_tag(self.default_tag, self.trans) self.__parse_record() self.__parse_trailer() for title, handle in self.inline_srcs.items(): @@ -3318,6 +3330,9 @@ class GedcomParser(UpdateCallback): # Add the default reference if no source has found self.__add_default_source(person) + # Add a default tag if provided + self.__add_default_tag(person) + self.__check_msgs(_("INDI (individual) Gramps ID %s") % person.get_gramps_id(), state, person) # commit the person to the database @@ -4579,6 +4594,9 @@ class GedcomParser(UpdateCallback): # add default reference if no reference exists self.__add_default_source(family) + # Add a default tag if provided + self.__add_default_tag(family) + self.__check_msgs(_("FAM (family) Gramps ID %s") % family.get_gramps_id(), state, family) # commit family to database @@ -6118,6 +6136,9 @@ class GedcomParser(UpdateCallback): # Add the default reference if no source has found self.__add_default_source(media) + # Add a default tag if provided + self.__add_default_tag(media) + self.__check_msgs(_("OBJE (multi-media object) Gramps ID %s") % media.get_gramps_id(), state, media) # commit the person to the database @@ -6931,6 +6952,9 @@ class GedcomParser(UpdateCallback): self.__undefined) state.msg += sub_state.msg + # Add a default tag if provided + self.__add_default_tag(new_note) + self.dbase.commit_note(new_note, self.trans, new_note.change) obj.add_note(new_note.get_handle()) @@ -7255,6 +7279,13 @@ class GedcomParser(UpdateCallback): self.dbase.add_citation(citation, self.trans) obj.add_citation(citation.handle) + def __add_default_tag(self, obj): + """ + Add the default tag to the object. + """ + if self.default_tag: + obj.add_tag(self.default_tag.handle) + def __subm_name(self, line, state): """ @param line: The current line in GedLine format