2393 lines
108 KiB
Python
2393 lines
108 KiB
Python
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2011 Michiel D. Nauta
|
|
#
|
|
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
|
|
"""
|
|
Unittest that tests that part of the merge process that influences other
|
|
objects than the objects merging.
|
|
"""
|
|
import unittest
|
|
import time
|
|
import os
|
|
from io import BytesIO
|
|
import difflib
|
|
import copy
|
|
import lxml.etree as ET
|
|
import tempfile
|
|
|
|
from gramps.plugins.lib.libgrampsxml import GRAMPS_XML_VERSION
|
|
from gramps.test.test_util import Gramps
|
|
from gramps.gen.user import User
|
|
from gramps.gen.const import DATA_DIR, USER_PLUGINS
|
|
from gramps.version import VERSION
|
|
from gramps.gen.lib import Name, Surname
|
|
from gramps.gen.const import GRAMPS_LOCALE as glocale
|
|
_ = glocale.translation.sgettext
|
|
|
|
HAS_CLIMERGE = os.path.isdir(os.path.join(USER_PLUGINS, 'CliMerge'))
|
|
HAS_EXPORTRAW = os.path.isdir(os.path.join(USER_PLUGINS, 'ExportRaw'))
|
|
NS_G = 'http://gramps-project.org/xml/%s/' % GRAMPS_XML_VERSION
|
|
NSP = "{%s}" % NS_G
|
|
|
|
|
|
@unittest.skipUnless(HAS_CLIMERGE and HAS_EXPORTRAW,
|
|
'These tests need the 3rd-party plugins "CliMerge" '
|
|
'and "ExportRaw".')
|
|
class BaseMergeCheck(unittest.TestCase):
|
|
""" Base class for the merge tests """
|
|
def base_setup(self):
|
|
"""Set up code needed by all tests."""
|
|
date = time.localtime(time.time())
|
|
# libxml2.keepBlanksDefault(0)
|
|
self.parser = ET.XMLParser(remove_blank_text=True)
|
|
styledoc = ET.parse(os.path.join(DATA_DIR, "gramps_canonicalize.xsl"),
|
|
parser=self.parser)
|
|
self.transform = ET.XSLT(styledoc)
|
|
self.basedoc = None
|
|
self.base_str = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE database PUBLIC "-//GRAMPS//DTD GRAMPS XML %s//EN"
|
|
"http://gramps-project.org/xml/%s/grampsxml.dtd">
|
|
<database xmlns="http://gramps-project.org/xml/%s/">
|
|
<header>
|
|
<created date="%04d-%02d-%02d" version="%s"/>
|
|
<researcher>\n </researcher>
|
|
</header>
|
|
""" % (GRAMPS_XML_VERSION, GRAMPS_XML_VERSION, GRAMPS_XML_VERSION,
|
|
date[0], date[1], date[2], VERSION)
|
|
|
|
def canonicalize(self, doctxt):
|
|
"""
|
|
Return a canonicalized string representation
|
|
|
|
:param doctxt: the text to bring in canonical form.
|
|
:type doctxt: either a string or an Xml document.
|
|
:returns: The text but in canonical form.
|
|
:rtype: string
|
|
"""
|
|
if isinstance(doctxt, bytes):
|
|
doc = ET.fromstring(doctxt, parser=self.parser)
|
|
elif isinstance(doctxt, ET._Element):
|
|
doc = doctxt
|
|
else:
|
|
raise TypeError
|
|
canonical_doc = self.transform(doc)
|
|
result = ET.tostring(canonical_doc, pretty_print=True)
|
|
#print(str(result, 'utf-8'))
|
|
return result
|
|
|
|
def do_case(self, phoenix_id, titanic_id, input_doc, expect_doc,
|
|
test_error_str=''):
|
|
"""Do the merge and "assert" the result."""
|
|
gramps = Gramps(user=User())
|
|
result_str, err_str = gramps.run(
|
|
'-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT',
|
|
'-i', '-', '-f', 'gramps', '-a', 'tool', '-p',
|
|
"name=climerge,primary=%s,secondary=%s" % (phoenix_id, titanic_id),
|
|
'-e', '-', '-f', 'gramps', stdin=BytesIO(input_doc), bytesio=True)
|
|
self.check_results(input_doc, expect_doc, result_str, err_str,
|
|
test_error_str)
|
|
|
|
def check_results(self, input_doc, expect_doc, result_str, err_str,
|
|
test_error_str=''):
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
|
input_file = os.path.join(tmpdirname, "merge_test_input.gramps")
|
|
|
|
if err_str:
|
|
if test_error_str:
|
|
self.assertIn(test_error_str, err_str)
|
|
return
|
|
else:
|
|
if "Traceback (most recent call last):" in err_str:
|
|
inp = self.canonicalize(input_doc)
|
|
inpt = open(input_file, mode='wb')
|
|
inpt.write(inp)
|
|
inpt.close()
|
|
raise Exception(err_str)
|
|
result = self.canonicalize(result_str)
|
|
result_file = os.path.join(tmpdirname, "merge_test_result.gramps")
|
|
expect = self.canonicalize(expect_doc)
|
|
expect_file = os.path.join(tmpdirname, "merge_test_expected.gramps")
|
|
|
|
if result != expect:
|
|
res = open(result_file, mode='wb')
|
|
res.write(result)
|
|
res.close()
|
|
eres = open(expect_file, mode='wb')
|
|
eres.write(expect)
|
|
eres.close()
|
|
inp = self.canonicalize(input_doc)
|
|
inpt = open(input_file, mode='wb')
|
|
inpt.write(inp)
|
|
inpt.close()
|
|
result = result.decode('utf-8')
|
|
expect = expect.decode('utf-8')
|
|
diff = difflib.ndiff(result, expect)
|
|
msg = ""
|
|
for line in diff:
|
|
msg += line
|
|
self.fail(msg)
|
|
|
|
def do_family_case(self, phoenix_id, titanic_id, father_h, mother_h,
|
|
input_doc, expect_doc, test_error_str=''):
|
|
gramps = Gramps(user=User())
|
|
result_str, err_str = gramps.run(
|
|
'-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT',
|
|
'-i', '-', '-f', 'gramps', '-a', 'tool', '-p',
|
|
"name=climerge,primary=%s,secondary=%s,father_h=%s,mother_h=%s" %
|
|
(phoenix_id, titanic_id, father_h, mother_h),
|
|
'-e', '-', '-f', 'gramps', stdin=BytesIO(input_doc), bytesio=True)
|
|
self.check_results(input_doc, expect_doc, result_str, err_str,
|
|
test_error_str)
|
|
|
|
def raw_contains(self, phoenix_id, titanic_id, input_doc, expect_str,
|
|
test_error_str=''):
|
|
gramps = Gramps(user=User())
|
|
result_str, err_str = gramps.run(
|
|
'-d', '.ImportXML', '--config=preferences.eprefix:DEFAULT',
|
|
'-i', '-', '-f', 'gramps', '-a', 'tool', '-p',
|
|
"name=climerge,primary=%s,secondary=%s" % (phoenix_id, titanic_id),
|
|
'-e', '-', '-f', 'raw', stdin=BytesIO(input_doc), bytesio=False)
|
|
if err_str:
|
|
if test_error_str:
|
|
self.assertIn(test_error_str, err_str)
|
|
return
|
|
if expect_str not in result_str:
|
|
msg = '\n***** result:\n' + result_str + \
|
|
'\n***** expect:\n' + expect_str
|
|
inp = self.canonicalize(input_doc)
|
|
with tempfile.TemporaryDirectory() as tmpdirname:
|
|
input_file = os.path.join(tmpdirname, "merge_test_input.gramps")
|
|
inpt = open(input_file, mode='wb')
|
|
inpt.write(inp)
|
|
inpt.close()
|
|
self.fail(msg)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# PersonCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class PersonCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on persons."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<events>
|
|
<event handle="_e0000" id="E0000">
|
|
<type>Birth</type>
|
|
<description>Event 0</description>
|
|
</event>
|
|
<event handle="_e0001" id="E0001">
|
|
<type>Birth</type>
|
|
<description>Event 1</description>
|
|
</event>
|
|
</events>
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
<eventref hlink="_e0000" role="Primary"/>
|
|
<lds_ord type="baptism">
|
|
<place hlink="_p0000"/>
|
|
</lds_ord>
|
|
<objref hlink="_o0000"/>
|
|
<noteref hlink="_n0000"/>
|
|
<citationref hlink="_c0000"/>
|
|
</person>
|
|
<person handle="_i0001" id="I0001">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 1</surname>
|
|
</name>
|
|
<eventref hlink="_e0001" role="Primary"/>
|
|
<lds_ord type="baptism">
|
|
<place hlink="_p0001"/>
|
|
</lds_ord>
|
|
<objref hlink="_o0001"/>
|
|
<noteref hlink="_n0001"/>
|
|
<citationref hlink="_c0001"/>
|
|
</person>
|
|
</people>
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
<reporef hlink="_r0001" medium="Electronic"/>
|
|
</source>
|
|
</sources>
|
|
<places>
|
|
<placeobj handle="_p0000" id="P0000" type="Country">
|
|
<ptitle>Place 0</ptitle>
|
|
<pname value="Place 0"/>
|
|
</placeobj>
|
|
<placeobj handle="_p0001" id="P0001" type="Country">
|
|
<ptitle>Place 1</ptitle>
|
|
<pname value="Place 1"/>
|
|
</placeobj>
|
|
</places>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
<repositories>
|
|
<repository handle="_r0000" id="R0000">
|
|
<rname>New York Public Library</rname>
|
|
<type>Library</type>
|
|
</repository>
|
|
<repository handle="_r0001" id="R0001">
|
|
<rname>Aunt Martha's Attic</rname>
|
|
<type>Collection</type>
|
|
</repository>
|
|
</repositories>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0.</text>
|
|
<style name="link" value="gramps://Citation/handle/c0001">
|
|
<range start="0" end="1"/>
|
|
</style>
|
|
<style name="link" value="gramps://Event/handle/e0001">
|
|
<range start="1" end="2"/>
|
|
</style>
|
|
<style name="link" value="gramps://Media/handle/o0001">
|
|
<range start="2" end="3"/>
|
|
</style>
|
|
<style name="link" value="gramps://Note/handle/n0001">
|
|
<range start="3" end="4"/>
|
|
</style>
|
|
<style name="link" value="gramps://Place/handle/p0001">
|
|
<range start="4" end="5"/>
|
|
</style>
|
|
<style name="link" value="gramps://Repository/handle/r0001">
|
|
<range start="5" end="6"/>
|
|
</style>
|
|
<style name="link" value="gramps://Source/handle/s0001">
|
|
<range start="6" end="7"/>
|
|
</style>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_event_merge(self):
|
|
"""Merge two events. Also checks that Event link in note is updated.
|
|
"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
eventref = expect.xpath("//g:person[@handle='_i0001']/g:eventref",
|
|
namespaces={"g": NS_G})[0]
|
|
eventref.attrib['hlink'] = '_e0000'
|
|
event = expect.xpath("//g:event[@handle='_e0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
event.getparent().remove(event)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[1]
|
|
notetag.attrib['value'] = "gramps://Event/handle/e0000"
|
|
self.do_case('E0000', 'E0001', self.basedoc, expect)
|
|
#print(str(ET.tostring(expect, pretty_print=True), 'utf-8'))
|
|
|
|
def test_place_merge(self):
|
|
"""Merge two places"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
place = expect.xpath("//g:person[@handle='_i0001']/g:lds_ord/g:place",
|
|
namespaces={"g": NS_G})[0]
|
|
place.attrib['hlink'] = '_p0000'
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
placeobj.getparent().remove(placeobj)
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[4]
|
|
notetag.attrib['value'] = "gramps://Place/handle/p0000"
|
|
ET.SubElement(placeobj, NSP + 'pname', value='Place 1')
|
|
self.do_case('P0000', 'P0001', self.basedoc, expect)
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
srcref = expect.xpath("//g:person[@handle='_i0001']/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
srcref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Citation/handle/c0000"
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_media_merge(self):
|
|
"""Merge two media objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
objref = expect.xpath("//g:person[@handle='_i0001']/g:objref",
|
|
namespaces={"g": NS_G})[0]
|
|
objref.attrib['hlink'] = '_o0000'
|
|
object_ = expect.xpath("//g:object[@handle='_o0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[2]
|
|
notetag.attrib['value'] = "gramps://Media/handle/o0000"
|
|
self.do_case('O0000', 'O0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:person[@handle='_i0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[3]
|
|
notetag.attrib['value'] = "gramps://Note/handle/n0000"
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
def test_repository_merge(self):
|
|
"""Merge two repository objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
reporef = expect.xpath("//g:source[@handle='_s0001']/g:reporef",
|
|
namespaces={"g": NS_G})[0]
|
|
reporef.attrib['hlink'] = '_r0000'
|
|
object_ = expect.xpath("//g:repository[@handle='_r0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[5]
|
|
notetag.attrib['value'] = "gramps://Repository/handle/r0000"
|
|
self.do_case('R0000', 'R0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# FamilyCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class FamilyCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on families."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<events>
|
|
<event handle="_e0000" id="E0000">
|
|
<type>Birth</type>
|
|
<description>Event 0</description>
|
|
</event>
|
|
<event handle="_e0001" id="E0001">
|
|
<type>Birth</type>
|
|
<description>Event 1</description>
|
|
</event>
|
|
</events>
|
|
<families>
|
|
<family handle="_f0000" id="F0000">
|
|
<rel type="Married"/>
|
|
<eventref hlink="_e0000" role="Family"/>
|
|
<lds_ord type="sealed_to_spouse">
|
|
<place hlink="_p0000"/>
|
|
</lds_ord>
|
|
<objref hlink="_o0000"/>
|
|
<noteref hlink="_n0000"/>
|
|
<citationref hlink="_c0000"/>
|
|
</family>
|
|
<family handle="_f0001" id="F0001">
|
|
<rel type="Married"/>
|
|
<eventref hlink="_e0001" role="Family"/>
|
|
<lds_ord type="sealed_to_spouse">
|
|
<place hlink="_p0001"/>
|
|
</lds_ord>
|
|
<objref hlink="_o0001"/>
|
|
<noteref hlink="_n0001"/>
|
|
<citationref hlink="_c0001"/>
|
|
</family>
|
|
</families>
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<places>
|
|
<placeobj handle="_p0000" id="P0000" type="Country">
|
|
<ptitle>Place 0</ptitle>
|
|
<pname value="Place 0"/>
|
|
</placeobj>
|
|
<placeobj handle="_p0001" id="P0001" type="Country">
|
|
<ptitle>Place 1</ptitle>
|
|
<pname value="Place 1"/>
|
|
</placeobj>
|
|
</places>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_event_merge(self):
|
|
"""Merge two events"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
eventref = expect.xpath("//g:family[@handle='_f0001']/g:eventref",
|
|
namespaces={"g": NS_G})[0]
|
|
eventref.attrib['hlink'] = '_e0000'
|
|
event = expect.xpath("//g:event[@handle='_e0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
event.getparent().remove(event)
|
|
self.do_case('E0000', 'E0001', self.basedoc, expect)
|
|
|
|
def test_place_merge(self):
|
|
"""Merge two places"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
place = expect.xpath("//g:family[@handle='_f0001']/g:lds_ord/g:place",
|
|
namespaces={"g": NS_G})[0]
|
|
place.attrib['hlink'] = '_p0000'
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
placeobj.getparent().remove(placeobj)
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(placeobj, NSP + 'pname', value='Place 1')
|
|
self.do_case('P0000', 'P0001', self.basedoc, expect)
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citref = expect.xpath("//g:family[@handle='_f0001']/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
citref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_media_merge(self):
|
|
"""Merge two media objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
objref = expect.xpath("//g:family[@handle='_f0001']/g:objref",
|
|
namespaces={"g": NS_G})[0]
|
|
objref.attrib['hlink'] = '_o0000'
|
|
object_ = expect.xpath("//g:object[@handle='_o0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
self.do_case('O0000', 'O0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:family[@handle='_f0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# EventCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class EventCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on events."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<events>
|
|
<event handle="_e0000" id="E0000">
|
|
<type>Birth</type>
|
|
<place hlink="_p0000"/>
|
|
<description>Event 0</description>
|
|
<noteref hlink="_n0000"/>
|
|
<citationref hlink="_c0000"/>
|
|
<objref hlink="_o0000"/>
|
|
</event>
|
|
<event handle="_e0001" id="E0001">
|
|
<type>Birth</type>
|
|
<place hlink="_p0001"/>
|
|
<description>Event 1</description>
|
|
<noteref hlink="_n0001"/>
|
|
<citationref hlink="_c0001"/>
|
|
<objref hlink="_o0001"/>
|
|
</event>
|
|
</events>
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<places>
|
|
<placeobj handle="_p0000" id="P0000" type = "Country">
|
|
<ptitle>Place 0</ptitle>
|
|
<pname value="Place 0"/>
|
|
</placeobj>
|
|
<placeobj handle="_p0001" id="P0001" type = "Country">
|
|
<ptitle>Place 1</ptitle>
|
|
<pname value="Place 1"/>
|
|
</placeobj>
|
|
</places>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_place_merge(self):
|
|
"""Merge two places"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
place = expect.xpath("//g:event[@handle='_e0001']/g:place",
|
|
namespaces={"g": NS_G})[0]
|
|
place.attrib['hlink'] = '_p0000'
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
placeobj.getparent().remove(placeobj)
|
|
placeobj = expect.xpath("//g:placeobj[@handle='_p0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(placeobj, NSP + 'pname', value='Place 1')
|
|
self.do_case('P0000', 'P0001', self.basedoc, expect)
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citref = expect.xpath("//g:event[@handle='_e0001']/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
citref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_media_merge(self):
|
|
"""Merge two media objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
objref = expect.xpath("//g:event[@handle='_e0001']/g:objref",
|
|
namespaces={"g": NS_G})[0]
|
|
objref.attrib['hlink'] = '_o0000'
|
|
object_ = expect.xpath("//g:object[@handle='_o0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
self.do_case('O0000', 'O0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:event[@handle='_e0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# PlaceCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class PlaceCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on places."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<places>
|
|
<placeobj handle="_p0000" id="P0000" type = "Country">
|
|
<ptitle>Place 0</ptitle>
|
|
<pname value="Place 0"/>
|
|
<objref hlink="_o0000"/>
|
|
<noteref hlink="_n0000"/>
|
|
<citationref hlink="_c0000"/>
|
|
</placeobj>
|
|
<placeobj handle="_p0001" id="P0001" type = "Country">
|
|
<ptitle>Place 1</ptitle>
|
|
<pname value="Place 1"/>
|
|
<objref hlink="_o0001"/>
|
|
<noteref hlink="_n0001"/>
|
|
<citationref hlink="_c0001"/>
|
|
</placeobj>
|
|
</places>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citref = expect.xpath("//g:placeobj[@handle='_p0001']/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
citref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_media_merge(self):
|
|
"""Merge two media objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
objref = expect.xpath("//g:placeobj[@handle='_p0001']/g:objref",
|
|
namespaces={"g": NS_G})[0]
|
|
objref.attrib['hlink'] = '_o0000'
|
|
object_ = expect.xpath("//g:object[@handle='_o0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
self.do_case('O0000', 'O0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:placeobj[@handle='_p0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# SourceCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class SourceCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on sources."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
<noteref hlink="_n0000"/>
|
|
<objref hlink="_o0000"/>
|
|
<reporef hlink="_r0000" medium="Book"/>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
<noteref hlink="_n0001"/>
|
|
<objref hlink="_o0001"/>
|
|
<reporef hlink="_r0001" medium="Book"/>
|
|
</source>
|
|
</sources>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
<repositories>
|
|
<repository handle="_r0000" id="R0000">
|
|
<rname>Repo 0</rname>
|
|
<type>Library</type>
|
|
</repository>
|
|
<repository handle="_r0001" id="R0001">
|
|
<rname>Repo 1</rname>
|
|
<type>Library</type>
|
|
</repository>
|
|
</repositories>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
#def test_citation_merge(self): SEE special cases.
|
|
|
|
def test_repo_merge(self):
|
|
"""Merge two repositories"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
# Adjust the repository reference in expectation.
|
|
reporef = expect.xpath("//g:source[@handle='_s0001']/g:reporef",
|
|
namespaces={"g": NS_G})[0]
|
|
reporef.attrib['hlink'] = '_r0000'
|
|
# Remove one repository in expectation.
|
|
repo = expect.xpath("//g:repository[@handle='_r0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
repo.getparent().remove(repo)
|
|
# Do the actual merger and comparison.
|
|
self.do_case('R0000', 'R0001', self.basedoc, expect)
|
|
|
|
def test_media_merge(self):
|
|
"""Merge two media objects"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
objref = expect.xpath("//g:source[@handle='_s0001']/g:objref",
|
|
namespaces={"g": NS_G})[0]
|
|
objref.attrib['hlink'] = '_o0000'
|
|
object_ = expect.xpath("//g:object[@handle='_o0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
object_.getparent().remove(object_)
|
|
self.do_case('O0000', 'O0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:source[@handle='_s0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# RepoCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class RepoCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on repositories."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<repositories>
|
|
<repository handle="_r0000" id="R0000">
|
|
<rname>Repo 0</rname>
|
|
<type>Library</type>
|
|
<address>
|
|
<city>Amsterdam</city>
|
|
<citationref hlink="_c0000"/>
|
|
</address>
|
|
<noteref hlink="_n0000"/>
|
|
</repository>
|
|
<repository handle="_r0001" id="R0001">
|
|
<rname>Repo 1</rname>
|
|
<type>Library</type>
|
|
<address>
|
|
<city>Rotterdam</city>
|
|
<citationref hlink="_c0001"/>
|
|
</address>
|
|
<noteref hlink="_n0001"/>
|
|
</repository>
|
|
</repositories>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citref = expect.xpath("//g:repository[@handle='_r0001']"
|
|
"/g:address/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
citref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:repository[@handle='_r0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# MediaCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class MediaCheck(BaseMergeCheck):
|
|
"""Class that checks what the influence is of merger of other primary
|
|
objects on media objects."""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
<noteref hlink="_n0000"/>
|
|
<citationref hlink="_c0000"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
<noteref hlink="_n0001"/>
|
|
<citationref hlink="_c0001"/>
|
|
</object>
|
|
</objects>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Event Note">
|
|
<text>Note 0</text>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Event Note">
|
|
<text>Note 1</text>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_citation_merge(self):
|
|
"""Merge two citations"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citref = expect.xpath("//g:object[@handle='_o0001']/g:citationref",
|
|
namespaces={"g": NS_G})[0]
|
|
citref.attrib['hlink'] = '_c0000'
|
|
citation = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
citation.getparent().remove(citation)
|
|
self.do_case('C0000', 'C0001', self.basedoc, expect)
|
|
|
|
def test_note_merge(self):
|
|
"""Merge two notes"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
noteref = expect.xpath("//g:object[@handle='_o0001']/g:noteref",
|
|
namespaces={"g": NS_G})[0]
|
|
noteref.attrib['hlink'] = '_n0000'
|
|
note = expect.xpath("//g:note[@handle='_n0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
note.getparent().remove(note)
|
|
self.do_case('N0000', 'N0001', self.basedoc, expect)
|
|
|
|
#=========================================================================
|
|
#
|
|
# Special cases
|
|
#
|
|
#=========================================================================
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# SourceSourceCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class SourceSourceCheck(BaseMergeCheck):
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<citations>
|
|
<citation handle="_c0000" id="C0000">
|
|
<page>p.10</page>
|
|
<confidence>2</confidence>
|
|
<objref hlink="_o0000">
|
|
<citationref hlink="_c0002"/>
|
|
</objref>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0001" id="C0001">
|
|
<page>p.11</page>
|
|
<confidence>2</confidence>
|
|
<objref hlink="_o0001">
|
|
<citationref hlink="_c0003"/>
|
|
</objref>
|
|
<sourceref hlink="_s0000"/>
|
|
</citation>
|
|
<citation handle="_c0002" id="C0002">
|
|
<page>p.12</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
<citation handle="_c0003" id="C0003">
|
|
<page>p.13</page>
|
|
<confidence>2</confidence>
|
|
<sourceref hlink="_s0001"/>
|
|
</citation>
|
|
</citations>
|
|
<sources>
|
|
<source handle="_s0000" id="S0000">
|
|
<stitle>Source 0</stitle>
|
|
</source>
|
|
<source handle="_s0001" id="S0001">
|
|
<stitle>Source 1</stitle>
|
|
</source>
|
|
</sources>
|
|
<objects>
|
|
<object handle="_o0000" id="O0000">
|
|
<file src="image0.jpg" mime="image/jpeg" description="Image 0"/>
|
|
</object>
|
|
<object handle="_o0001" id="O0001">
|
|
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
|
|
</object>
|
|
</objects>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_citation_merge(self):
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
citrefs = expect.xpath("//g:citation[@handle='_c0001']"
|
|
"/g:objref/g:citationref",
|
|
namespaces={"g": NS_G})
|
|
citrefs[0].attrib['hlink'] = '_c0002'
|
|
citations = expect.xpath("//g:citation[@handle='_c0003']",
|
|
namespaces={"g": NS_G})
|
|
citations[0].getparent().remove(citations[0])
|
|
self.do_case('C0002', 'C0003', self.basedoc, expect)
|
|
|
|
def test_citation_cross_merge(self):
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
input_citrefs = input_ctxt.xpath("//g:citation/g:objref/g:citationref",
|
|
namespaces={"g": NS_G})
|
|
input_citrefs[0].attrib['hlink'] = '_c0001'
|
|
input_citrefs[1].attrib['hlink'] = '_c0000'
|
|
rmcit = input_ctxt.xpath("//g:citation[@handle='_c0002']",
|
|
namespaces={"g": NS_G})
|
|
rmcit[0].getparent().remove(rmcit[0])
|
|
rmcit = input_ctxt.xpath("//g:citation[@handle='_c0003']",
|
|
namespaces={"g": NS_G})
|
|
rmcit[0].getparent().remove(rmcit[0])
|
|
rmsrc = input_ctxt.xpath("//g:source[@handle='_s0001']",
|
|
namespaces={"g": NS_G})
|
|
rmsrc[0].getparent().remove(rmsrc[0])
|
|
expect = copy.deepcopy(input_ctxt)
|
|
citrefs = expect.xpath("//g:citation[@handle='_c0000']/g:objref"
|
|
"/g:citationref", namespaces={"g": NS_G})
|
|
citrefs[0].attrib['hlink'] = '_c0000'
|
|
# add objref
|
|
objref = expect.xpath("//g:citation[@handle='_c0000']/g:objref",
|
|
namespaces={"g": NS_G})
|
|
objref2 = expect.xpath("//g:citation[@handle='_c0001']/g:objref",
|
|
namespaces={"g": NS_G})
|
|
#objref[0].addNextSibling(objref2[0])
|
|
objref[0].addnext(objref2[0])
|
|
# remove citation
|
|
citations = expect.xpath("//g:citation[@handle='_c0001']",
|
|
namespaces={"g": NS_G})
|
|
citations[0].getparent().remove(citations[0])
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('C0000', 'C0001', input_doc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# BirthCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class BirthCheck(BaseMergeCheck):
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<events>
|
|
<event handle="_e0000" id="E0000">
|
|
<type>Baptism</type>
|
|
<description>Event 0</description>
|
|
</event>
|
|
<event handle="_e0001" id="E0001">
|
|
<type>Birth</type>
|
|
<description>Event 1</description>
|
|
</event>
|
|
<event handle="_e0002" id="E0002">
|
|
<type>Death</type>
|
|
<description>Event 2</description>
|
|
</event>
|
|
</events>
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
<eventref hlink="_e0000" role="Primary"/>
|
|
<eventref hlink="_e0001" role="Primary"/>
|
|
<eventref hlink="_e0002" role="Primary"/>
|
|
</person>
|
|
</people>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
surname = Surname()
|
|
surname.set_surname("Person 0")
|
|
surname.set_prefix("")
|
|
surname.set_connector("")
|
|
name = Name()
|
|
name.add_surname(surname)
|
|
self.expect_str = "person: i0000 ('i0000', 'I0000', 1, %s, [], " % str(
|
|
name.serialize())
|
|
|
|
def test_birth_loss(self):
|
|
# check that birth_ref_index is -1
|
|
# input_doc = ET.fromstring(self.basedoc, parser=self.parser)
|
|
expect_str = self.expect_str + "1, -1"
|
|
self.raw_contains('E0000', 'E0001', self.basedoc, expect_str)
|
|
|
|
def test_second_birth(self):
|
|
# check that birth _ref_index is 2
|
|
input_doc = ET.fromstring(self.basedoc, parser=self.parser)
|
|
events = input_doc.xpath("//g:events",
|
|
namespaces={"g": NS_G})[0]
|
|
second_birth = ET.SubElement(events, NSP + 'event',
|
|
handle='_e0003', id='E0003')
|
|
birthtype = ET.SubElement(second_birth, NSP + 'type')
|
|
birthtype.text = 'Birth'
|
|
birthdesc = ET.SubElement(second_birth, NSP + 'description')
|
|
birthdesc.text = 'Event 3'
|
|
person = input_doc.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'eventref', hlink='_e0003', role='Primary')
|
|
expect_str = self.expect_str + "1, 2"
|
|
input_str = ET.tostring(input_doc)
|
|
self.raw_contains('E0000', 'E0001', input_str, expect_str)
|
|
|
|
def test_death_merge(self):
|
|
# check that death_ref_index is -1
|
|
expect_str = self.expect_str + "-1, 1"
|
|
self.raw_contains('E0000', 'E0002', self.basedoc, expect_str)
|
|
|
|
def test_second_death(self):
|
|
# check that death _ref_index is 2
|
|
input_doc = ET.fromstring(self.basedoc, parser=self.parser)
|
|
events = input_doc.xpath("//g:events",
|
|
namespaces={"g": NS_G})[0]
|
|
second_death = ET.SubElement(events, NSP + 'event',
|
|
handle='_e0003', id='E0003')
|
|
deathtype = ET.SubElement(second_death, NSP + 'type')
|
|
deathtype.text = 'Death'
|
|
deathdesc = ET.SubElement(second_death, NSP + 'description')
|
|
deathdesc.text = 'Event 3'
|
|
person = input_doc.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'eventref', hlink='_e0003', role='Primary')
|
|
expect_str = self.expect_str + "2, 1"
|
|
input_str = ET.tostring(input_doc)
|
|
self.raw_contains('E0000', 'E0002', input_str, expect_str)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# PersonPersonCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class PersonPersonCheck(BaseMergeCheck):
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
</person>
|
|
<person handle="_i0001" id="I0001">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 1</surname>
|
|
</name>
|
|
</person>
|
|
</people>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Person Note">
|
|
<text>Note 0.</text>
|
|
<style name="link" value="gramps://Person/handle/i0001">
|
|
<range start="0" end="1"/>
|
|
</style>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_person_merge(self):
|
|
"""There is a person not involved in merger that references Titanic."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
people = input_ctxt.xpath("//g:people",
|
|
namespaces={"g": NS_G})[0]
|
|
person = ET.SubElement(people, NSP + 'person',
|
|
handle='_i0002', id='I0002')
|
|
ET.SubElement(person, NSP + 'gender').text = 'M'
|
|
name = ET.SubElement(person, NSP + 'name', type='Birth Name')
|
|
ET.SubElement(name, NSP + 'surname').text = 'Person 2'
|
|
ET.SubElement(person, NSP + 'personref',
|
|
hlink='_i0001', rel='Neighbour')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
personref = expect.xpath("//g:personref",
|
|
namespaces={"g": NS_G})[0]
|
|
personref.attrib['hlink'] = '_i0000'
|
|
person = expect.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
altname = ET.SubElement(person, NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 1'
|
|
ET.SubElement(person, NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0001')
|
|
person = expect.xpath("//g:person[@handle='_i0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
person.getparent().remove(person)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0001', input_doc, expect)
|
|
|
|
def test_person_cross(self):
|
|
"""Phoenix has ref to Titanic and vice versa"""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[0], NSP + 'personref',
|
|
hlink='_i0001', rel='Neighbour East')
|
|
ET.SubElement(persons[1], NSP + 'personref',
|
|
hlink='_i0000', rel='Neighbour West')
|
|
|
|
expect = copy.deepcopy(input_ctxt)
|
|
personref = expect.xpath("//g:person[@handle='_i0000']/g:personref",
|
|
namespaces={"g": NS_G})[0]
|
|
personref.attrib['hlink'] = '_i0000'
|
|
person = expect.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
altname = ET.SubElement(person, NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 1'
|
|
ET.SubElement(person, NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0001')
|
|
person.append(personref) # restore order of elements
|
|
personref = ET.SubElement(person, NSP + 'personref',
|
|
hlink='_i0000', rel='Neighbour West')
|
|
person = expect.xpath("//g:person[@handle='_i0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
person.getparent().remove(person)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0001', input_doc, expect)
|
|
|
|
def test_person_self(self):
|
|
"""Titanic references itself"""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'personref',
|
|
hlink='_i0001', rel='Neighbour')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
person = expect.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
altname = ET.SubElement(person, NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 1'
|
|
ET.SubElement(person, NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0001')
|
|
ET.SubElement(person, NSP + 'personref',
|
|
hlink='_i0000', rel='Neighbour')
|
|
person = expect.xpath("//g:person[@handle='_i0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
person.getparent().remove(person)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0001', input_doc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# ParentFamilyPersonCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class ParentFamilyPersonCheck(BaseMergeCheck):
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
<childof hlink="_f0000"/>
|
|
</person>
|
|
<person handle="_i0001" id="I0001">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 1</surname>
|
|
</name>
|
|
<childof hlink="_f0001"/>
|
|
</person>
|
|
</people>
|
|
<families>
|
|
<family handle="_f0000" id="F0000">
|
|
<rel type="Unknown"/>
|
|
<childref hlink="_i0000"/>
|
|
</family>
|
|
<family handle="_f0001" id="F0001">
|
|
<rel type="Unknown"/>
|
|
<childref hlink="_i0001"/>
|
|
</family>
|
|
</families>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_person_merge(self):
|
|
"""Merge two persons that are children in some family"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
childref = expect.xpath("//g:family[@handle='_f0001']/g:childref",
|
|
namespaces={"g": NS_G})[0]
|
|
childref.attrib['hlink'] = '_i0000'
|
|
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 1'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0001')
|
|
childof = expect.xpath("//g:person[@handle='_i0000']/g:childof",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(childof) # restore order of elements
|
|
childof = ET.SubElement(persons[0], NSP + 'childof', hlink='_f0001')
|
|
persons[1].getparent().remove(persons[1])
|
|
self.do_case('I0000', 'I0001', self.basedoc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# FamilyPersonCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class FamilyPersonCheck(BaseMergeCheck):
|
|
"""Merge two persons that are parents in families"""
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
<parentin hlink="_f0000"/>
|
|
</person>
|
|
<person handle="_i0001" id="I0001">
|
|
<gender>F</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 1</surname>
|
|
</name>
|
|
<parentin hlink="_f0000"/>
|
|
</person>
|
|
<person handle="_i0002" id="I0002">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 2</surname>
|
|
</name>
|
|
</person>
|
|
<person handle="_i0003" id="I0003">
|
|
<gender>F</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 3</surname>
|
|
</name>
|
|
</person>
|
|
</people>
|
|
<families>
|
|
<family handle="_f0000" id="F0000">
|
|
<rel type="Unknown"/>
|
|
<father hlink="_i0000"/>
|
|
<mother hlink="_i0001"/>
|
|
</family>
|
|
<family handle="_f0001" id="F0001">
|
|
<rel type="Unknown"/>
|
|
</family>
|
|
</families>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Person Note">
|
|
<text>Note 0</text>
|
|
<style name="link" value="gramps://Person/handle/i0002">
|
|
<range start="0" end="2"/>
|
|
</style>
|
|
</note>
|
|
<note handle="_n0001" id="N0001" type="Family Note">
|
|
<text>Note 1</text>
|
|
<style name="link" value="gramps://Family/handle/f0001">
|
|
<range start="0" end="4"/>
|
|
</style>
|
|
</note>
|
|
<note handle="_n0003" id="N0003" type="Person Note">
|
|
<text>Note 0</text>
|
|
<style name="link" value="gramps://Person/handle/i0003">
|
|
<range start="0" end="2"/>
|
|
</style>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_titanic_no_fam(self):
|
|
"Test merge of two persons where titanic is not a parent in a family"
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
self.do_case('I0000', 'I0002', self.basedoc, expect)
|
|
|
|
def test_no_fam_merge(self):
|
|
"""Test merge of two persons, both parents in a family, but such that
|
|
the families will not merge."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0002']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0001')
|
|
father = expect.xpath("//g:family[@handle='_f0001']/g:father",
|
|
namespaces={"g": NS_G})[0]
|
|
father.attrib['hlink'] = '_i0000'
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
def test_multi_rel(self):
|
|
"""Merge two persons where titanic has multiple family relationships
|
|
with his partner, this should raise an error."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0002')
|
|
ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0002')
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0000')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
families = expect.xpath("//g:families",
|
|
namespaces={"g": NS_G})[0]
|
|
family = ET.SubElement(families, NSP + 'family',
|
|
handle='_f0002', id='F0002')
|
|
ET.SubElement(family, NSP + 'rel', type='Married')
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0000')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0002')
|
|
ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0002')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
families = input_ctxt.xpath("//g:families",
|
|
namespaces={"g": NS_G})[0]
|
|
family = ET.SubElement(families, NSP + 'family',
|
|
handle='_f0002', id='F0002')
|
|
ET.SubElement(family, NSP + 'rel', type='Married')
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect,
|
|
test_error_str="")
|
|
|
|
def test_merge_fam(self):
|
|
"""Merge two persons such that also the families in which they are
|
|
parents get merged."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[1], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0001')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
parentin = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[1]
|
|
parentin.getparent().remove(parentin)
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
def test_fam_none_merge(self):
|
|
"""Merge two persons, both father in families without mothers."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0002']",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin = ET.SubElement(person, NSP + 'parentin', hlink='_f0001', )
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
# Can't think of a testcase that would merge multiple families.
|
|
|
|
def test_fam_mother_merge(self):
|
|
"""Merge two persons that are mothers in their respective families."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[0], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(persons[3], NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0000')
|
|
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
notetag = expect.xpath("//g:note[@handle='_n0003']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0001"
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
parentin = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[1]
|
|
parentin.getparent().remove(parentin)
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0001', 'I0003', input_doc, expect)
|
|
|
|
def test_childref_notyet(self):
|
|
"""Merge two people leading to merger of families that have children.
|
|
"""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[1], NSP + 'childof', hlink='_f0000')
|
|
families = input_ctxt.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(families[0], NSP + 'childref', hlink='_i0001')
|
|
parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(families[1], NSP + 'father', hlink='_i0002')
|
|
ET.SubElement(persons[3], NSP + 'childof', hlink='_f0001')
|
|
ET.SubElement(families[1], NSP + 'childref', hlink='_i0003')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
families = expect.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
families[1].getparent().remove(families[1])
|
|
childof = expect.xpath("//g:person[@handle='_i0003']/g:childof",
|
|
namespaces={"g": NS_G})[0]
|
|
childof.attrib['hlink'] = '_f0000'
|
|
ET.SubElement(families[0], NSP + 'childref', hlink='_i0003')
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
def test_childref_already(self):
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(persons[1], NSP + 'childof', hlink='_f0000')
|
|
ET.SubElement(persons[1], NSP + 'childof', hlink='_f0001')
|
|
families = input_ctxt.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
ET.SubElement(families[0], NSP + 'childref', hlink='_i0001')
|
|
parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
ET.SubElement(families[1], NSP + 'father', hlink='_i0002')
|
|
ET.SubElement(families[1], NSP + 'childref', hlink='_i0001')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
families = expect.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
families[1].getparent().remove(families[1])
|
|
childof = expect.xpath("//g:person[@handle='_i0001']/g:childof",
|
|
namespaces={"g": NS_G})
|
|
childof[1].getparent().remove(childof[1])
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
def test_ldsord(self):
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ldsord = ET.SubElement(persons[3], NSP + 'lds_ord',
|
|
type='sealed_to_parents')
|
|
ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name', alt='1',
|
|
type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
families = expect.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
families[1].getparent().remove(families[1])
|
|
sealedto = expect.xpath("//g:sealed_to",
|
|
namespaces={"g": NS_G})[0]
|
|
sealedto.attrib['hlink'] = '_f0000'
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
# This test fails because the assigment of family ids shifts F0000 to F0001
|
|
# and F0001 to F0002!
|
|
def test_ldsord_cross(self):
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ldsord = ET.SubElement(persons[0], NSP + 'lds_ord',
|
|
type='sealed_to_parents')
|
|
ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
ldsord = expect.xpath("//g:lds_ord",
|
|
namespaces={"g": NS_G})[0]
|
|
altname.addnext(ldsord) # restore order of elements
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
families = expect.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
families[1].getparent().remove(families[1])
|
|
sealedto = expect.xpath("//g:sealed_to",
|
|
namespaces={"g": NS_G})[0]
|
|
sealedto.attrib['hlink'] = '_f0000'
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
def test_ldsord_self(self):
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
persons = input_ctxt.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
parentin = ET.SubElement(persons[2], NSP + 'parentin', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'father', hlink='_i0002')
|
|
ldsord = ET.SubElement(persons[2], NSP + 'lds_ord',
|
|
type='sealed_to_parents')
|
|
ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
ldsord = expect.xpath("//g:lds_ord",
|
|
namespaces={"g": NS_G})[0]
|
|
altname.addnext(ldsord) # restore order of elements
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Person/handle/i0000"
|
|
persons[2].getparent().remove(persons[2])
|
|
notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
|
|
namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
families = expect.xpath("//g:family",
|
|
namespaces={"g": NS_G})
|
|
families[1].getparent().remove(families[1])
|
|
sealedto = expect.xpath("//g:sealed_to",
|
|
namespaces={"g": NS_G})[0]
|
|
sealedto.attrib['hlink'] = '_f0000'
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_case('I0000', 'I0002', input_doc, expect)
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
#
|
|
# FamilyMergeCheck class
|
|
#
|
|
#-------------------------------------------------------------------------
|
|
class FamilyMergeCheck(BaseMergeCheck):
|
|
def setUp(self):
|
|
self.base_setup()
|
|
base_str = """
|
|
<people>
|
|
<person handle="_i0000" id="I0000">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 0</surname>
|
|
</name>
|
|
<parentin hlink="_f0000"/>
|
|
</person>
|
|
<person handle="_i0001" id="I0001">
|
|
<gender>F</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 1</surname>
|
|
</name>
|
|
<parentin hlink="_f0000"/>
|
|
</person>
|
|
<person handle="_i0002" id="I0002">
|
|
<gender>M</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 2</surname>
|
|
</name>
|
|
<parentin hlink="_f0001"/>
|
|
</person>
|
|
<person handle="_i0003" id="I0003">
|
|
<gender>F</gender>
|
|
<name type="Birth Name">
|
|
<surname>Person 3</surname>
|
|
</name>
|
|
<parentin hlink="_f0001"/>
|
|
</person>
|
|
</people>
|
|
<families>
|
|
<family handle="_f0000" id="F0000">
|
|
<rel type="Unknown"/>
|
|
<father hlink="_i0000"/>
|
|
<mother hlink="_i0001"/>
|
|
</family>
|
|
<family handle="_f0001" id="F0001">
|
|
<rel type="Unknown"/>
|
|
<father hlink="_i0002"/>
|
|
<mother hlink="_i0003"/>
|
|
</family>
|
|
</families>
|
|
<notes>
|
|
<note handle="_n0000" id="N0000" type="Family Note">
|
|
<text>Note 0.</text>
|
|
<style name="link" value="gramps://Family/handle/f0001">
|
|
<range start="0" end="1"/>
|
|
</style>
|
|
</note>
|
|
</notes>
|
|
</database>"""
|
|
self.basedoc = bytes(bytearray(self.base_str + base_str,
|
|
encoding='utf-8'))
|
|
|
|
def test_father_son_merge(self):
|
|
"""Merge two families where the fathers have a father-son relationship
|
|
so that an error is raised."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0002']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0000')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0002')
|
|
input_doc = expect = ET.tostring(input_ctxt)
|
|
self.do_family_case(
|
|
'F0000', 'F0001', 'i0000', 'i0001', input_doc, expect,
|
|
test_error_str=_("A parent and child cannot be merged. To merge "
|
|
"these people, you must first break the "
|
|
"relationship between them."))
|
|
|
|
def test_child_parent_merge_no_father(self):
|
|
"""Merge two families where the phoenix family has no father and
|
|
the father of the titanic family is a child of the phoenix family.
|
|
"""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0002']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
father = input_ctxt.xpath("//g:family[@handle='_f0001']/g:father",
|
|
namespaces={"g": NS_G})[0]
|
|
father.getparent().remove(father)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0000')
|
|
input_doc = expect = ET.tostring(input_ctxt)
|
|
self.do_family_case(
|
|
'F0000', 'F0001', 'i0000', 'i0001', input_doc, expect,
|
|
test_error_str=_("A parent and child cannot be merged. To merge "
|
|
"these people, you must first break the "
|
|
"relationship between them."))
|
|
|
|
def test_child_parent_merge_no_father_swapped(self):
|
|
"""Merge two families where the phoenix family has no father and
|
|
the father of the titanic family, which is the phoenix-father, is a
|
|
child of the phoenix family."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
father = input_ctxt.xpath("//g:family[@handle='_f0000']/g:father",
|
|
namespaces={"g": NS_G})[0]
|
|
father.getparent().remove(father)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0002']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0000')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0002')
|
|
input_doc = expect = ET.tostring(input_ctxt)
|
|
self.do_family_case(
|
|
'F0000', 'F0001', 'i0002', 'i0001', input_doc, expect,
|
|
test_error_str=_("A parent and child cannot be merged. To merge "
|
|
"these people, you must first break the "
|
|
"relationship between them."))
|
|
|
|
def test_regular_merge(self):
|
|
"""Merge two families succesfully"""
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[2].getparent().remove(persons[2])
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
|
|
self.basedoc, expect)
|
|
|
|
def test_father_swapped(self):
|
|
"Merge two families where the phoenix-father is of the titanic family."
|
|
expect = ET.fromstring(self.basedoc, parser=self.parser)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[2], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 0'
|
|
attr = ET.SubElement(persons[2], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0000')
|
|
parentref = expect.xpath("//g:person[@handle='_i0002']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentref.attrib['hlink'] = '_f0000'
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[0].getparent().remove(persons[0])
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
father = expect.xpath("//g:family[@handle='_f0000']/g:father",
|
|
namespaces={"g": NS_G})[0]
|
|
father.attrib['hlink'] = '_i0002'
|
|
self.do_family_case('F0000', 'F0001', 'i0002', 'i0001',
|
|
self.basedoc, expect)
|
|
|
|
#def test_mother_swapped(self):
|
|
|
|
def test_no_father(self):
|
|
"""Merge two families, where one family has not father"""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0002']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
father = input_ctxt.xpath("//g:family[@handle='_f0001']/g:father",
|
|
namespaces={"g": NS_G})[0]
|
|
father.getparent().remove(father)
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
|
|
input_doc, expect)
|
|
|
|
def test_no_mother_swapped(self):
|
|
"""Merge two families where one family has no mother and the
|
|
phoenix-mother is from the titanic family."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.getparent().remove(parentin)
|
|
mother = input_ctxt.xpath("//g:family[@handle='_f0000']/g:mother",
|
|
namespaces={"g": NS_G})[0]
|
|
mother.getparent().remove(mother)
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[2].getparent().remove(persons[2])
|
|
parentin = expect.xpath("//g:person[@handle='_i0003']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
parentin.attrib['hlink'] = '_f0000'
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
family = expect.xpath("//g:family[@handle='_f0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
mother = ET.SubElement(family, NSP + 'mother', hlink='_i0003')
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0003',
|
|
input_doc, expect)
|
|
|
|
#def test_no_parents(self):
|
|
|
|
def test_childref_notyet(self):
|
|
"""Merge two families with non-duplicate child references."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
people = input_ctxt.xpath("//g:people",
|
|
namespaces={"g": NS_G})[0]
|
|
person = ET.SubElement(people, NSP + 'person',
|
|
handle='_i0004', id='_I0004')
|
|
ET.SubElement(person, NSP + 'gender').text = 'M'
|
|
name = ET.SubElement(person, NSP + 'name', type='Birth Name')
|
|
ET.SubElement(name, NSP + 'surname').text = 'Person 4'
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0004')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[2].getparent().remove(persons[2])
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
childof = expect.xpath("//g:person[@handle='_i0004']/g:childof",
|
|
namespaces={"g": NS_G})[0]
|
|
childof.attrib['hlink'] = '_f0000'
|
|
family = expect.xpath("//g:family[@handle='_f0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0004')
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
|
|
input_doc, expect)
|
|
|
|
def test_childref_already(self):
|
|
"""Merge two families with duplicate child references."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
people = input_ctxt.xpath("//g:people",
|
|
namespaces={"g": NS_G})[0]
|
|
person = ET.SubElement(people, NSP + 'person',
|
|
handle='_i0004', id='_I0004')
|
|
ET.SubElement(person, NSP + 'gender').text = 'M'
|
|
name = ET.SubElement(person, NSP + 'name', type='Birth Name')
|
|
ET.SubElement(name, NSP + 'surname').text = 'Person 4'
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0000')
|
|
ET.SubElement(person, NSP + 'childof', hlink='_f0001')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0004')
|
|
family = input_ctxt.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
ET.SubElement(family, NSP + 'childref', hlink='_i0004')
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[2].getparent().remove(persons[2])
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
childof = expect.xpath("//g:person[@handle='_i0004']/g:childof",
|
|
namespaces={"g": NS_G})[1]
|
|
childof.getparent().remove(childof)
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
|
|
input_doc, expect)
|
|
|
|
# this test fails because the families get IDs F0001 and F0002!
|
|
def test_ldsord(self):
|
|
"""Merge two families where one person has a reference to the
|
|
titanic family."""
|
|
input_ctxt = ET.fromstring(self.basedoc, parser=self.parser)
|
|
person = input_ctxt.xpath("//g:person[@handle='_i0000']",
|
|
namespaces={"g": NS_G})[0]
|
|
ldsord = ET.SubElement(person, NSP + 'lds_ord',
|
|
type='sealed_to_parents')
|
|
ET.SubElement(ldsord, NSP + 'sealed_to', hlink='_f0001')
|
|
parentin = input_ctxt.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
ldsord.addnext(parentin)
|
|
expect = copy.deepcopy(input_ctxt)
|
|
persons = expect.xpath("//g:person",
|
|
namespaces={"g": NS_G})
|
|
altname = ET.SubElement(persons[0], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 2'
|
|
ldsord = expect.xpath("//g:lds_ord",
|
|
namespaces={"g": NS_G})[0]
|
|
altname.addnext(ldsord) # restore order of elements
|
|
attr = ET.SubElement(persons[0], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0002')
|
|
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[2].getparent().remove(persons[2])
|
|
altname = ET.SubElement(persons[1], NSP + 'name',
|
|
alt='1', type='Birth Name')
|
|
ET.SubElement(altname, NSP + 'surname').text = 'Person 3'
|
|
attr = ET.SubElement(persons[1], NSP + 'attribute',
|
|
type='Merged Gramps ID', value='I0003')
|
|
parentref = expect.xpath("//g:person[@handle='_i0001']/g:parentin",
|
|
namespaces={"g": NS_G})[0]
|
|
attr.addnext(parentref) # restore order of elements
|
|
persons[3].getparent().remove(persons[3])
|
|
family = expect.xpath("//g:family[@handle='_f0001']",
|
|
namespaces={"g": NS_G})[0]
|
|
family.getparent().remove(family)
|
|
notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
|
|
notetag.attrib['value'] = "gramps://Family/handle/f0000"
|
|
sealedto = expect.xpath("//g:sealed_to",
|
|
namespaces={"g": NS_G})[0]
|
|
sealedto.attrib['hlink'] = '_f0000'
|
|
input_doc = ET.tostring(input_ctxt)
|
|
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
|
|
input_doc, expect)
|
|
|
|
|
|
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()
|