diff --git a/gramps/gen/db/dictionary.py b/gramps/gen/db/dictionary.py index 8595e8b9f..c5623c2f9 100644 --- a/gramps/gen/db/dictionary.py +++ b/gramps/gen/db/dictionary.py @@ -195,6 +195,7 @@ class DictionaryDb(DbWriteBase, DbReadBase): } # skip GEDCOM cross-ref check for now: self.set_feature("skip-check-xref", True) + self.set_feature("skip-import-additions", True) self.readonly = False self.db_is_open = True self.name_formats = [] @@ -255,6 +256,20 @@ class DictionaryDb(DbWriteBase, DbReadBase): self.txn = DictionaryTxn("DbDictionary Transaction", self) self.transaction = None + def version_supported(self): + """Return True when the file has a supported version.""" + return True + + def get_table_names(self): + """Return a list of valid table names.""" + return list(self._tables.keys()) + + def get_table_metadata(self, table_name): + """Return the metadata for a valid table name.""" + if table_name in self._tables: + return self._tables[table_name] + return None + def transaction_commit(self, txn): pass diff --git a/gramps/gen/lib/citation.py b/gramps/gen/lib/citation.py index 77408cc30..3be1c145f 100644 --- a/gramps/gen/lib/citation.py +++ b/gramps/gen/lib/citation.py @@ -44,6 +44,7 @@ from .mediabase import MediaBase from .notebase import NoteBase from .datebase import DateBase from ..constfunc import cuni +from .handle import Handle #------------------------------------------------------------------------- # @@ -112,12 +113,12 @@ class Citation(MediaBase, NoteBase, PrimaryObject, DateBase): :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, # 0 + return {"handle": Handle("Citation", self.handle), # 0 "gramps_id": self.gramps_id, # 1 "date": DateBase.to_struct(self), # 2 "page": cuni(self.page), # 3 "confidence": self.confidence, # 4 - "source_handle": self.source_handle, # 5 + "source_handle": Handle("Source", self.source_handle), # 5 "note_list": NoteBase.to_struct(self), # 6 "media_list": MediaBase.to_struct(self), # 7 "datamap": self.datamap, # 8 diff --git a/gramps/gen/lib/citationbase.py b/gramps/gen/lib/citationbase.py index af015fd36..233faa6e2 100644 --- a/gramps/gen/lib/citationbase.py +++ b/gramps/gen/lib/citationbase.py @@ -34,6 +34,13 @@ CitationBase class for GRAMPS. import logging LOG = logging.getLogger(".citation") +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +from .handle import Handle + #------------------------------------------------------------------------- # # CitationBase class @@ -89,7 +96,7 @@ class CitationBase(object): :returns: Returns a struct containing the data of the object. :rtype: list """ - return self.citation_list + return [Handle("Citation", c) for c in self.citation_list] def unserialize(self, data): """ diff --git a/gramps/gen/lib/event.py b/gramps/gen/lib/event.py index aeecd2f3d..42ebeec9f 100644 --- a/gramps/gen/lib/event.py +++ b/gramps/gen/lib/event.py @@ -47,6 +47,7 @@ from .attrbase import AttributeBase from .datebase import DateBase from .placebase import PlaceBase from .eventtype import EventType +from .handle import Handle #------------------------------------------------------------------------- # @@ -137,12 +138,12 @@ class Event(CitationBase, NoteBase, MediaBase, AttributeBase, :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Event", self.handle), "gramps_id": self.gramps_id, "type": self.__type.to_struct(), "date": DateBase.to_struct(self), "description": self.__description, - "place": self.place, + "place": Handle("Place", self.place), "citation_list": CitationBase.to_struct(self), "note_list": NoteBase.to_struct(self), "media_list": MediaBase.to_struct(self), diff --git a/gramps/gen/lib/family.py b/gramps/gen/lib/family.py index d11e470e9..525421237 100644 --- a/gramps/gen/lib/family.py +++ b/gramps/gen/lib/family.py @@ -52,6 +52,7 @@ from .tagbase import TagBase from .childref import ChildRef from .familyreltype import FamilyRelType from .const import IDENTICAL, EQUAL, DIFFERENT +from .handle import Handle #------------------------------------------------------------------------- # @@ -148,10 +149,10 @@ class Family(CitationBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Family", self.handle), "gramps_id": self.gramps_id, - "father_handle": self.father_handle, - "mother_handle": self.mother_handle, + "father_handle": Handle("Person", self.father_handle), + "mother_handle": Handle("Person", self.mother_handle), "child_ref_list": [cr.to_struct() for cr in self.child_ref_list], "type": self.type.to_struct(), "event_ref_list": [er.to_struct() for er in self.event_ref_list], diff --git a/gramps/gen/lib/handle.py b/gramps/gen/lib/handle.py new file mode 100644 index 000000000..342d248c9 --- /dev/null +++ b/gramps/gen/lib/handle.py @@ -0,0 +1,31 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2013 Doug Blank +# +# 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$ + +class Handle: + def __init__(self, classname, handle): + """ Class to hold type and handle of referenced item """ + self.classname = classname + self.handle = handle + + def __repr__(self): + return "Handle(%s, %s)" % (self.classname, self.handle) + diff --git a/gramps/gen/lib/mediaobj.py b/gramps/gen/lib/mediaobj.py index ac434a295..c42b4b861 100644 --- a/gramps/gen/lib/mediaobj.py +++ b/gramps/gen/lib/mediaobj.py @@ -52,6 +52,7 @@ from .notebase import NoteBase from .datebase import DateBase from .attrbase import AttributeBase from .tagbase import TagBase +from .handle import Handle #------------------------------------------------------------------------- # @@ -140,7 +141,7 @@ class MediaObject(CitationBase, NoteBase, DateBase, AttributeBase, :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Media", self.handle), "gramps_id": self.gramps_id, "path": self.path, "mime": self.mime, diff --git a/gramps/gen/lib/note.py b/gramps/gen/lib/note.py index 36ce336a5..7e39bb1e4 100644 --- a/gramps/gen/lib/note.py +++ b/gramps/gen/lib/note.py @@ -36,6 +36,7 @@ from .tagbase import TagBase from .notetype import NoteType from .styledtext import StyledText from ..constfunc import cuni +from .handle import Handle #------------------------------------------------------------------------- # @@ -118,7 +119,7 @@ class Note(BasicPrimaryObject, TagBase): :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Note", self.handle), "gramps_id": self.gramps_id, "text": self.text.to_struct(), "format": self.format, diff --git a/gramps/gen/lib/notebase.py b/gramps/gen/lib/notebase.py index 6ce3324c6..efaee269d 100644 --- a/gramps/gen/lib/notebase.py +++ b/gramps/gen/lib/notebase.py @@ -25,6 +25,8 @@ NoteBase class for GRAMPS. """ +from .handle import Handle + #------------------------------------------------------------------------- # # NoteBase class @@ -73,7 +75,7 @@ class NoteBase(object): :returns: Returns a struct containing the data of the object. :rtype: list """ - return self.note_list + return [Handle("Note", n) for n in self.note_list] def unserialize(self, data): """ diff --git a/gramps/gen/lib/person.py b/gramps/gen/lib/person.py index b0af41d24..3cbe49430 100644 --- a/gramps/gen/lib/person.py +++ b/gramps/gen/lib/person.py @@ -51,6 +51,7 @@ from .attribute import Attribute from .const import IDENTICAL, EQUAL, DIFFERENT from ..ggettext import gettext as _ from ..constfunc import STRTYPE +from .handle import Handle #------------------------------------------------------------------------- # @@ -182,7 +183,7 @@ class Person(CitationBase, NoteBase, AttributeBase, MediaBase, :rtype: dict """ return { - "handle": self.handle, # 0 + "handle": Handle("Person", self.handle), # 0 "gramps_id": self.gramps_id, # 1 "gender": self.gender, # 2 "primary_name": self.primary_name.to_struct(), # 3 @@ -192,8 +193,10 @@ class Person(CitationBase, NoteBase, AttributeBase, MediaBase, "birth_ref_index": self.birth_ref_index, # 6 "event_ref_list": [er.to_struct() for er in self.event_ref_list], # 7 - "family_list": self.family_list, # 8 - "parent_family_list": self.parent_family_list, # 9 + "family_list": [Handle("Family", f) for f in + self.family_list], # 8 + "parent_family_list": [Handle("Family", f) for f in + self.parent_family_list], # 9 "media_list": MediaBase.to_struct(self), # 10 "address_list": AddressBase.to_struct(self), # 11 "attribute_list": AttributeBase.to_struct(self), # 12 diff --git a/gramps/gen/lib/place.py b/gramps/gen/lib/place.py index 88b05ba96..4022c98cf 100644 --- a/gramps/gen/lib/place.py +++ b/gramps/gen/lib/place.py @@ -38,6 +38,7 @@ from .notebase import NoteBase from .mediabase import MediaBase from .urlbase import UrlBase from .location import Location +from .handle import Handle _EMPTY_LOC = Location().serialize() @@ -135,7 +136,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject): else: main_loc = self.main_loc.to_struct() - return {"handle": self.handle, + return {"handle": Handle("Place", self.handle), "gramps_id": self.gramps_id, "title": self.title, "long": self.long, diff --git a/gramps/gen/lib/refbase.py b/gramps/gen/lib/refbase.py index 679698455..d1042e280 100644 --- a/gramps/gen/lib/refbase.py +++ b/gramps/gen/lib/refbase.py @@ -24,6 +24,8 @@ Base Reference class for GRAMPS. """ +from .handle import Handle + #------------------------------------------------------------------------- # # RefBase class @@ -68,7 +70,7 @@ class RefBase(object): :returns: Returns a struct containing the data of the object. :rtype: str """ - return self.ref + return [Handle(*t) for t in self.get_referenced_handles()] def unserialize(self, data): """ diff --git a/gramps/gen/lib/repo.py b/gramps/gen/lib/repo.py index c6768679f..648e6abae 100644 --- a/gramps/gen/lib/repo.py +++ b/gramps/gen/lib/repo.py @@ -37,6 +37,7 @@ from .addressbase import AddressBase from .urlbase import UrlBase from .repotype import RepositoryType from ..constfunc import cuni +from .handle import Handle #------------------------------------------------------------------------- # @@ -88,7 +89,7 @@ class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject): :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Repository", self.handle), "gramps_id": self.gramps_id, "type": self.type.to_struct(), "name": cuni(self.name), diff --git a/gramps/gen/lib/src.py b/gramps/gen/lib/src.py index e6566769d..3b4c916de 100644 --- a/gramps/gen/lib/src.py +++ b/gramps/gen/lib/src.py @@ -37,6 +37,7 @@ from .notebase import NoteBase from .reporef import RepoRef from .const import DIFFERENT, EQUAL, IDENTICAL from ..constfunc import cuni +from .handle import Handle #------------------------------------------------------------------------- # @@ -90,7 +91,7 @@ class Source(MediaBase, NoteBase, PrimaryObject): :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Source", self.handle), "gramps_id": self.gramps_id, "title": cuni(self.title), "author": cuni(self.author), diff --git a/gramps/gen/lib/tag.py b/gramps/gen/lib/tag.py index 6f094c4b3..1054117be 100644 --- a/gramps/gen/lib/tag.py +++ b/gramps/gen/lib/tag.py @@ -30,6 +30,7 @@ Tag object for GRAMPS. # #------------------------------------------------------------------------- from .tableobj import TableObject +from .handle import Handle #------------------------------------------------------------------------- # @@ -219,7 +220,7 @@ class Tag(TableObject): :returns: Returns a struct containing the data of the object. :rtype: dict """ - return {"handle": self.handle, + return {"handle": Handle("Tag", self.handle), "name": self.__name, "color": self.__color, "priority": self.__priority, diff --git a/gramps/gen/lib/tagbase.py b/gramps/gen/lib/tagbase.py index e5af60e48..f4aef89ad 100644 --- a/gramps/gen/lib/tagbase.py +++ b/gramps/gen/lib/tagbase.py @@ -23,6 +23,9 @@ """ TagBase class for Gramps. """ + +from .handle import Handle + #------------------------------------------------------------------------- # # TagBase class @@ -74,7 +77,7 @@ class TagBase(object): :returns: Returns a struct containing the data of the object. :rtype: list """ - return self.tag_list + return [Handle('Tag', t) for t in self.tag_list] def unserialize(self, data): """ diff --git a/gramps/gen/merge/diff.py b/gramps/gen/merge/diff.py index 46541baa2..84e010269 100644 --- a/gramps/gen/merge/diff.py +++ b/gramps/gen/merge/diff.py @@ -23,7 +23,6 @@ """ This package implements an object difference engine. """ -from __future__ import print_function import os @@ -32,12 +31,14 @@ from ..dbstate import DbState from gramps.cli.grampscli import CLIManager from ..plug import BasePluginManager from ..db.dictionary import DictionaryDb +from gramps.gen.lib.handle import Handle -def import_as_dict(filename): +def import_as_dict(filename, user=None): """ Import the filename into a DictionaryDb and return it. """ - user = User() + if user is None: + user = User() db = DictionaryDb() dbstate = DbState() climanager = CLIManager(dbstate, False) # do not load db_loader @@ -54,9 +55,9 @@ def import_as_dict(filename): name, error_tuple, pdata = item # (filename, (exception-type, exception, traceback), pdata) etype, exception, traceback = error_tuple - print("ERROR:", name, exception) + #print("ERROR:", name, exception) return False - retval = import_function = getattr(mod, pdata.import_function) + import_function = getattr(mod, pdata.import_function) import_function(db, filename, user) return db return None @@ -65,8 +66,8 @@ def diff_dates(json1, json2): """ Compare two json date objects. Returns True if different. """ - if json1 == json2: - return False + if json1 == json2: # if same, then Not Different + return False # else, they still might be Not Different elif isinstance(json1, dict) and isinstance(json2, dict): if json1["dateval"] == json2["dateval"] and json2["dateval"] != 0: return False @@ -83,43 +84,43 @@ def diff_items(path, json1, json2): """ if json1 == json2: return False + elif isinstance(json1, Handle) and isinstance(json2, Handle): + return not (json1.classname == json2.classname and + json1.handle == json2.handle) + elif isinstance(json1, list) and isinstance(json2, list): + if len(json1) != len(json2): + return True + else: + pos = 0 + for v1, v2 in zip(json1, json2): + result = diff_items(path + ("[%d]" % pos), v1, v2) + if result: + return True + pos += 1 + return False elif isinstance(json1, dict) and isinstance(json2, dict): - retval = False for key in json1.keys(): if key == "change": continue # don't care about time differences, only data changes elif key == "date": result = diff_dates(json1["date"], json2["date"]) if result: - retval = result + #print("different dates", path) + #print(" old:", json1["date"]) + #print(" new:", json2["date"]) + return True else: - value1 = json1[key] - value2 = json2[key] - if isinstance(value1, dict) and isinstance(value2, dict): - result = diff_items(path + "." + key, value1, value2) - if result: - retval = True - elif isinstance(value1, list) and isinstance(value2, list): - pos = 0 - for v1, v2 in zip(value1, value2): - result = diff_items(path + "." + key + ("[%d]" % pos), - v1, v2) - if result: - retval = True - pos += 1 - elif value1 != value2: - print("different parts", path + "." + key) - print(" old:", value1) - print(" new:", value2) - retval = True - return retval + result = diff_items(path + "." + key, json1[key], json2[key]) + if result: + return True + return False else: - print("different values", path) - print(" old:", json1) - print(" new:", json2) + #print("different values", path) + #print(" old:", json1) + #print(" new:", json2) return True -def diff_dbs(db1, db2): +def diff_dbs(db1, db2, user=None): """ 1. new objects => mark for insert 2. deleted objects, no change locally after delete date => mark @@ -129,11 +130,16 @@ def diff_dbs(db1, db2): 4. updated objects => do a diff on differences, mark origin values as new data """ + if user is None: + user = User() missing_from_old = [] missing_from_new = [] diffs = [] + user.begin_progress(_('Family Tree Differences'), + _('Searching...'), 10) for item in ['Person', 'Family', 'Source', 'Citation', 'Event', 'Media', 'Place', 'Repository', 'Note', 'Tag']: + user.step_progress() handles1 = sorted(db1._tables[item]["handles_func"]()) handles2 = sorted(db2._tables[item]["handles_func"]()) p1 = 0 @@ -164,12 +170,15 @@ def diff_dbs(db1, db2): item2 = db2._tables[item]["handle_func"](handles2[p2]) missing_from_old += [(item, item2)] p2 += 1 + user.end_progress() return diffs, missing_from_old, missing_from_new -def diff_db_to_file(old_db, filename): +def diff_db_to_file(old_db, filename, user=None): + if user is None: + user = User() # First, get data as a DictionaryDb - new_db = import_as_dict(filename) + new_db = import_as_dict(filename, user, user) # Next get differences: - diffs, m_old, m_new = diff_dbs(old_db, new_db) + diffs, m_old, m_new = diff_dbs(old_db, new_db, user) return diffs, m_old, m_new diff --git a/gramps/gen/simple/_simpleaccess.py b/gramps/gen/simple/_simpleaccess.py index d26a0b4f4..07fefbb50 100644 --- a/gramps/gen/simple/_simpleaccess.py +++ b/gramps/gen/simple/_simpleaccess.py @@ -28,7 +28,7 @@ Provide a simplified database access interface to the GRAMPS database. from __future__ import with_statement, unicode_literals from ..lib import (Person, Family, Event, Source, Place, Citation, - MediaObject, Repository, Note, Date) + MediaObject, Repository, Note, Date, Tag) from ..datehandler import displayer from ..utils.string import gender as gender_map from ..utils.db import get_birth_or_fallback, get_death_or_fallback @@ -937,6 +937,9 @@ class SimpleAccess(object): return "%s: %s [%s]" % (_(object_class), obj.type, self.gid(obj)) + elif isinstance(obj, Tag): + return "%s: [%s]" % (_(object_class), + obj.name) else: return "Error: incorrect object class: '%s'" % type(obj) else: @@ -944,40 +947,42 @@ class SimpleAccess(object): else: return "Error: invalid object class: '%s'" % object_class - def describe(self, obj): + def describe(self, obj, prop=None, value=None): """ Given a object, return a string describing the object. """ + if prop and value: + obj = self.dbase.get_table_metadata(obj)[prop + "_func"](value) if isinstance(obj, Person): - return self.name(obj) + return "%s [%s]" % (self.name(obj), + self.gid(obj)) elif isinstance(obj, Event): - return self.event_type(obj) + return "%s [%s]" % (self.event_type(obj), + self.gid(obj)) elif isinstance(obj, Family): - father = self.father(obj) - mother = self.mother(obj) - if father: - father_text = self.name(father) - else: - father_text = _("Unknown father") - if mother: - mother_text = self.name(mother) - else: - mother_text = _("Unknown mother") - return "%s and %s" % (mother_text, father_text) + return "%s/%s [%s]" % (self.name(self.mother(obj)), + self.name(self.father(obj)), + self.gid(obj)) elif isinstance(obj, MediaObject): - return obj.desc - elif isinstance(obj, Citation): - return obj.gramps_id + return "%s [%s]" % (obj.desc, + self.gid(obj)) elif isinstance(obj, Source): - return self.title(obj) + return "%s [%s]" % (self.title(obj), + self.gid(obj)) + elif isinstance(obj, Citation): + return "[%s]" % (self.gid(obj)) elif isinstance(obj, Place): - return place_name(self.dbase, obj.handle) + return "%s [%s]" % (place_name(self.dbase, + obj.handle), + self.gid(obj)) elif isinstance(obj, Repository): - return obj.gramps_id + return "%s [%s]" % (obj.type, + self.gid(obj)) elif isinstance(obj, Note): - return obj.gramps_id - elif obj is None: - return "" + return "%s [%s]" % (obj.type, + self.gid(obj)) + elif isinstance(obj, Tag): + return "[%s]" % (obj.name) else: return "Error: incorrect object class: '%s'" % type(obj) diff --git a/gramps/plugins/importer/importcsv.py b/gramps/plugins/importer/importcsv.py index e11fb14cd..fb22ea224 100644 --- a/gramps/plugins/importer/importcsv.py +++ b/gramps/plugins/importer/importcsv.py @@ -146,8 +146,11 @@ 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, (config.get('preferences.tag-on-import-format') if - config.get('preferences.tag-on-import') else None)) + if dbase.get_feature("skip-import-additions"): # don't add source or tags + parser = CSVParser(dbase, user, None) + else: + 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) diff --git a/gramps/plugins/importer/importgedcom.py b/gramps/plugins/importer/importgedcom.py index cdcccd886..ac5ee084a 100644 --- a/gramps/plugins/importer/importgedcom.py +++ b/gramps/plugins/importer/importgedcom.py @@ -114,11 +114,15 @@ def importData(database, filename, user): if code_set: stage_one.set_encoding(code_set) ifile.seek(0) - gedparse = libgedcom.GedcomParser( - 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)) + if database.get_feature("skip-import-additions"): # don't add source or tags + gedparse = libgedcom.GedcomParser( + database, ifile, filename, user, stage_one, None, None) + else: + gedparse = libgedcom.GedcomParser( + 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/importxml.py b/gramps/plugins/importer/importxml.py index fdaeaff36..9cf089f19 100644 --- a/gramps/plugins/importer/importxml.py +++ b/gramps/plugins/importer/importxml.py @@ -124,9 +124,12 @@ def importData(database, filename, user): change = time.time() else: change = os.path.getmtime(filename) - parser = GrampsParser(database, user, change, - (config.get('preferences.tag-on-import-format') if - config.get('preferences.tag-on-import') else None)) + if database.get_feature("skip-import-additions"): # don't add source or tags + parser = GrampsParser(database, user, change, None) + else: + 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)