6960: Error merging citations
Back-port my work on this bug from gramps40. Unfortunately, the tests on gramps34 still fail (they pass on gramps40/trunk), but the repro steps marked by Nick in the bug succeed, so I shall stop here :) svn: r22965
This commit is contained in:
parent
b0d5343d24
commit
6da4a00375
@ -42,6 +42,9 @@ import gen.lib
|
|||||||
from gen.lib import Name, Surname
|
from gen.lib import Name, Surname
|
||||||
from gen.ggettext import sgettext as _
|
from gen.ggettext import sgettext as _
|
||||||
|
|
||||||
|
HAS_CLIMERGE = os.path.isdir(os.path.join(USER_PLUGINS, 'CliMerge'))
|
||||||
|
HAS_EXPORTRAW = os.path.isdir(os.path.join(USER_PLUGINS, 'ExportRaw'))
|
||||||
|
|
||||||
class CopiedDoc(object):
|
class CopiedDoc(object):
|
||||||
"""Context manager that creates a deep copy of an libxml-xml document."""
|
"""Context manager that creates a deep copy of an libxml-xml document."""
|
||||||
def __init__(self, xmldoc):
|
def __init__(self, xmldoc):
|
||||||
@ -72,6 +75,8 @@ class XpathContext(object):
|
|||||||
self.ctxt.xpathFreeContext()
|
self.ctxt.xpathFreeContext()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@unittest.skipUnless(HAS_CLIMERGE and HAS_EXPORTRAW,
|
||||||
|
'These tests need the 3rd-party plugins "CliMerge" and "ExportRaw".')
|
||||||
class BaseMergeCheck(unittest.TestCase):
|
class BaseMergeCheck(unittest.TestCase):
|
||||||
def base_setup(self):
|
def base_setup(self):
|
||||||
"""Set up code needed by all tests."""
|
"""Set up code needed by all tests."""
|
||||||
@ -136,6 +141,9 @@ class BaseMergeCheck(unittest.TestCase):
|
|||||||
if test_error_str:
|
if test_error_str:
|
||||||
self.assertIn(test_error_str, err_str)
|
self.assertIn(test_error_str, err_str)
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
if "Traceback (most recent call last):" in err_str:
|
||||||
|
raise Exception(err_str)
|
||||||
if debug:
|
if debug:
|
||||||
print('input :', self.canonicalize(input_doc))
|
print('input :', self.canonicalize(input_doc))
|
||||||
print('result:', self.canonicalize(result_str))
|
print('result:', self.canonicalize(result_str))
|
||||||
@ -2223,4 +2231,11 @@ class FamilyMergeCheck(BaseMergeCheck):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
if not HAS_CLIMERGE:
|
||||||
|
print('This program needs the third party "CliMerge" plugin.', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
if not HAS_EXPORTRAW:
|
||||||
|
print('This program needs the third party "ExportRaw" plugin.', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -43,13 +43,15 @@ from gen.lib.primaryobj import PrimaryObject
|
|||||||
from gen.lib.mediabase import MediaBase
|
from gen.lib.mediabase import MediaBase
|
||||||
from gen.lib.notebase import NoteBase
|
from gen.lib.notebase import NoteBase
|
||||||
from gen.lib.datebase import DateBase
|
from gen.lib.datebase import DateBase
|
||||||
|
from gen.lib.citationbase import IndirectCitationBase
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Citation class
|
# Citation class
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class Citation(MediaBase, NoteBase, PrimaryObject, DateBase):
|
class Citation(MediaBase, NoteBase, IndirectCitationBase,
|
||||||
|
PrimaryObject, DateBase):
|
||||||
"""
|
"""
|
||||||
A record of a citation of a source of information.
|
A record of a citation of a source of information.
|
||||||
|
|
||||||
@ -192,6 +194,16 @@ class Citation(MediaBase, NoteBase, PrimaryObject, DateBase):
|
|||||||
"""
|
"""
|
||||||
return self.media_list
|
return self.media_list
|
||||||
|
|
||||||
|
def get_citation_child_list(self):
|
||||||
|
"""
|
||||||
|
Return the list of child secondary objects that may refer citations.
|
||||||
|
|
||||||
|
:returns: Returns the list of child secondary child objects that may
|
||||||
|
refer citations.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
return self.media_list
|
||||||
|
|
||||||
def get_handle_referents(self):
|
def get_handle_referents(self):
|
||||||
"""
|
"""
|
||||||
Return the list of child objects which may, directly or through
|
Return the list of child objects which may, directly or through
|
||||||
|
@ -234,3 +234,56 @@ class CitationBase(object):
|
|||||||
|
|
||||||
for item in self.get_citation_child_list():
|
for item in self.get_citation_child_list():
|
||||||
item.replace_citation_references(old_handle, new_handle)
|
item.replace_citation_references(old_handle, new_handle)
|
||||||
|
|
||||||
|
class IndirectCitationBase(object):
|
||||||
|
"""
|
||||||
|
Citation management logic for objects that don't have citations
|
||||||
|
for the primary objects, but only for the child (secondary) ones.
|
||||||
|
|
||||||
|
The derived class must implement get_citation_child_list method
|
||||||
|
to return the list of child secondary objects that may refer
|
||||||
|
citations.
|
||||||
|
|
||||||
|
Note: for most objects, this functionality is inherited from
|
||||||
|
CitationBase, which checks both the object and the child objects.
|
||||||
|
"""
|
||||||
|
def has_citation_reference(self, citation_handle):
|
||||||
|
"""
|
||||||
|
Return True if any of the child objects has reference to this citation
|
||||||
|
handle.
|
||||||
|
|
||||||
|
:param citation_handle: The citation handle to be checked.
|
||||||
|
:type citation_handle: str
|
||||||
|
:returns: Returns whether any of it's child objects has reference to
|
||||||
|
this citation handle.
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
for item in self.get_citation_child_list():
|
||||||
|
if item.has_citation_reference(citation_handle):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def replace_citation_references(self, old_handle, new_handle):
|
||||||
|
"""
|
||||||
|
Replace references to citation handles in
|
||||||
|
all child objects and merge equivalent entries.
|
||||||
|
|
||||||
|
:param old_handle: The citation handle to be replaced.
|
||||||
|
:type old_handle: str
|
||||||
|
:param new_handle: The citation handle to replace the old one with.
|
||||||
|
:type new_handle: str
|
||||||
|
"""
|
||||||
|
for item in self.get_citation_child_list():
|
||||||
|
item.replace_citation_references(old_handle, new_handle)
|
||||||
|
|
||||||
|
def remove_citation_references(self, citation_handle_list):
|
||||||
|
"""
|
||||||
|
Remove references to all citation handles in the list in all child
|
||||||
|
objects.
|
||||||
|
|
||||||
|
:param citation_handle_list: The list of citation handles to be removed.
|
||||||
|
:type citation_handle_list: list
|
||||||
|
"""
|
||||||
|
for item in self.get_citation_child_list():
|
||||||
|
item.remove_citation_references(citation_handle_list)
|
||||||
|
@ -38,13 +38,15 @@ from gen.lib.attrbase import AttributeBase
|
|||||||
from gen.lib.refbase import RefBase
|
from gen.lib.refbase import RefBase
|
||||||
from gen.lib.eventroletype import EventRoleType
|
from gen.lib.eventroletype import EventRoleType
|
||||||
from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT
|
from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT
|
||||||
|
from gen.lib.citationbase import IndirectCitationBase
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Event References for Person/Family
|
# Event References for Person/Family
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class EventRef(SecondaryObject, PrivacyBase, NoteBase, AttributeBase, RefBase):
|
class EventRef(IndirectCitationBase,
|
||||||
|
SecondaryObject, PrivacyBase, NoteBase, AttributeBase, RefBase):
|
||||||
"""
|
"""
|
||||||
Event reference class.
|
Event reference class.
|
||||||
|
|
||||||
@ -152,47 +154,6 @@ class EventRef(SecondaryObject, PrivacyBase, NoteBase, AttributeBase, RefBase):
|
|||||||
"""
|
"""
|
||||||
return self.get_citation_child_list()
|
return self.get_citation_child_list()
|
||||||
|
|
||||||
def has_citation_reference(self, citation_handle) :
|
|
||||||
"""
|
|
||||||
Return True if any of the child objects has reference to this citation
|
|
||||||
handle.
|
|
||||||
|
|
||||||
:param citation_handle: The citation handle to be checked.
|
|
||||||
:type citation_handle: str
|
|
||||||
:returns: Returns whether any of it's child objects has reference to
|
|
||||||
this citation handle.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
if item.has_citation_reference(citation_handle):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove_citation_references(self, citation_handle_list):
|
|
||||||
"""
|
|
||||||
Remove references to all citation handles in the list in all child
|
|
||||||
objects.
|
|
||||||
|
|
||||||
:param citation_handle_list: The list of citation handles to be removed.
|
|
||||||
:type citation_handle_list: list
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
item.remove_citation_references(citation_handle_list)
|
|
||||||
|
|
||||||
def replace_citation_references(self, old_handle, new_handle):
|
|
||||||
"""
|
|
||||||
Replace references to citation handles in the list in this object and
|
|
||||||
all child objects and merge equivalent entries.
|
|
||||||
|
|
||||||
:param old_handle: The citation handle to be replaced.
|
|
||||||
:type old_handle: str
|
|
||||||
:param new_handle: The citation handle to replace the old one with.
|
|
||||||
:type new_handle: str
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
item.replace_citation_references(old_handle, new_handle)
|
|
||||||
|
|
||||||
def is_equivalent(self, other):
|
def is_equivalent(self, other):
|
||||||
"""
|
"""
|
||||||
Return if this eventref is equivalent, that is agrees in handle and
|
Return if this eventref is equivalent, that is agrees in handle and
|
||||||
|
@ -36,13 +36,15 @@ from gen.lib.notebase import NoteBase
|
|||||||
from gen.lib.addressbase import AddressBase
|
from gen.lib.addressbase import AddressBase
|
||||||
from gen.lib.urlbase import UrlBase
|
from gen.lib.urlbase import UrlBase
|
||||||
from gen.lib.repotype import RepositoryType
|
from gen.lib.repotype import RepositoryType
|
||||||
|
from gen.lib.citationbase import IndirectCitationBase
|
||||||
|
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Repository class
|
# Repository class
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject):
|
class Repository(NoteBase, AddressBase, UrlBase, IndirectCitationBase,
|
||||||
|
PrimaryObject):
|
||||||
"""A location where collections of Sources are found."""
|
"""A location where collections of Sources are found."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -140,59 +142,6 @@ class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject):
|
|||||||
"""
|
"""
|
||||||
return self.get_referenced_note_handles()
|
return self.get_referenced_note_handles()
|
||||||
|
|
||||||
def has_citation_reference(self, citation_handle) :
|
|
||||||
"""
|
|
||||||
Return True if any of the child objects has reference to this citation
|
|
||||||
handle.
|
|
||||||
|
|
||||||
Note: for most objects, this is inherited from citationbase, which
|
|
||||||
checks both the object and the child objects. However, uniquely,
|
|
||||||
Repositories do not have citations for the primary object, but only for
|
|
||||||
child (secondary) objects. Hence, this function has to be implemented
|
|
||||||
directly in the primary object; it only checks the child objects.
|
|
||||||
|
|
||||||
:param citation_handle: The citation handle to be checked.
|
|
||||||
:type citation_handle: str
|
|
||||||
:returns: Returns whether any of it's child objects has reference to
|
|
||||||
this citation handle.
|
|
||||||
:rtype: bool
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
if item.has_citation_reference(citation_handle):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove_citation_references(self, citation_handle_list):
|
|
||||||
"""
|
|
||||||
Remove references to all citation handles in the list in all child
|
|
||||||
objects.
|
|
||||||
|
|
||||||
Note: the same comment about citationbase in has_citation_reference
|
|
||||||
applies here too.
|
|
||||||
|
|
||||||
:param citation_handle_list: The list of citation handles to be removed.
|
|
||||||
:type citation_handle_list: list
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
item.remove_citation_references(citation_handle_list)
|
|
||||||
|
|
||||||
def replace_citation_references(self, old_handle, new_handle):
|
|
||||||
"""
|
|
||||||
Replace references to citation handles in the list in this object and
|
|
||||||
all child objects and merge equivalent entries.
|
|
||||||
|
|
||||||
Note: the same comment about citationbase in has_citation_reference
|
|
||||||
applies here too.
|
|
||||||
|
|
||||||
:param old_handle: The citation handle to be replaced.
|
|
||||||
:type old_handle: str
|
|
||||||
:param new_handle: The citation handle to replace the old one with.
|
|
||||||
:type new_handle: str
|
|
||||||
"""
|
|
||||||
for item in self.get_citation_child_list():
|
|
||||||
item.replace_citation_references(old_handle, new_handle)
|
|
||||||
|
|
||||||
def merge(self, acquisition):
|
def merge(self, acquisition):
|
||||||
"""
|
"""
|
||||||
Merge the content of acquisition into this repository.
|
Merge the content of acquisition into this repository.
|
||||||
|
Loading…
Reference in New Issue
Block a user