Files
gramps/gramps/gen/merge/test/merge_ref_test.py
Vassilii Khachaturov dacf5f4d3b flags HAS_CLIMERGE and HAS_EXPORTRAW to skip tests
Reapply from trunk my commit r22948

svn: r22961
2013-08-30 19:00:23 +00:00

2237 lines
99 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""
Unittest that tests that part of the merge process that influences other
objects than the objects merging.
"""
from __future__ import print_function, unicode_literals
import unittest
import time
import os
import subprocess
import libxml2
import libxslt
from gramps.plugins.lib.libgrampsxml import GRAMPS_XML_VERSION
from ...const import ROOT_DIR, USER_PLUGINS
from ....version import VERSION
from ...lib import Name, Surname
from ...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'))
class CopiedDoc(object):
"""Context manager that creates a deep copy of an libxml-xml document."""
def __init__(self, xmldoc):
self.xmldoc = xmldoc
self.copy = libxml2.readDoc(str(self.xmldoc), '', None,
libxml2.XML_PARSE_NONET)
def __enter__(self):
return self.copy
def __exit__(self, exc_type, exc_val, exc_tb):
self.copy.freeDoc()
return False
class XpathContext(object):
"""Context manager that creates a libxml2 xpath context that allows
evaluation of xpath expressions."""
def __init__(self, xmldoc):
self.xmldoc = xmldoc
self.ctxt = self.xmldoc.xpathNewContext()
self.ctxt.xpathRegisterNs('g', 'http://gramps-project.org/xml/%s/' %
GRAMPS_XML_VERSION)
def __enter__(self):
return self.ctxt
def __exit__(self, exc_type, exc_val, exc_tb):
self.ctxt.xpathFreeContext()
return False
@unittest.skipUnless(HAS_CLIMERGE and HAS_EXPORTRAW,
'These tests need the 3rd-party plugins "CliMerge" and "ExportRaw".')
class BaseMergeCheck(unittest.TestCase):
def base_setup(self):
"""Set up code needed by all tests."""
date = time.localtime(time.time())
libxml2.keepBlanksDefault(0)
styledoc = libxml2.parseFile(os.path.join(ROOT_DIR,
"../data/gramps_canonicalize.xsl"))
self.style = libxslt.parseStylesheetDoc(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 tearDown(self):
self.style.freeStylesheet()
self.basedoc.freeDoc()
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
"""
result = ''
if isinstance(doctxt, basestring):
doc = libxml2.readDoc(doctxt, '', None, libxml2.XML_PARSE_NONET)
elif isinstance(doctxt, libxml2.xmlDoc):
doc = doctxt
else:
raise TypeError
param = {}
canonical_doc = self.style.applyStylesheet(doc, param)
result = self.style.saveResultToString(canonical_doc)
canonical_doc.freeDoc()
if isinstance(doctxt, basestring):
doc.freeDoc()
return result
def do_test(self, phoenix_id, titanic_id, input_doc, expect_doc,
test_error_str='', debug=False):
"""Do the merge and "assert" the result."""
process = subprocess.Popen('python Gramps.py -d .ImportXML '
'--config=preferences.eprefix:DEFAULT '
'-i - -f gramps -a tool '
'-p "name=climerge,primary=%s,secondary=%s" '
'-e - -f gramps' % (phoenix_id, titanic_id),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
result_str, err_str = process.communicate(str(input_doc))
if err_str:
if test_error_str:
self.assertIn(test_error_str, err_str)
return
if debug:
print('input :', self.canonicalize(input_doc))
print('result:', self.canonicalize(result_str))
print('expect:', self.canonicalize(expect_doc))
self.assertEqual(self.canonicalize(result_str),
self.canonicalize(expect_doc))
def do_family_test(self, phoenix_id, titanic_id, father_h, mother_h,
input_doc, expect_doc, test_error_str='', debug=False):
process = subprocess.Popen('python Gramps.py -d .ImportXML '
'--config=preferences.eprefix:DEFAULT '
'-i - -f gramps -a tool '
'-p "name=climerge,primary=%s,secondary=%s,father_h=%s,mother_h=%s" '
'-e - -f gramps' % (phoenix_id, titanic_id, father_h, mother_h),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
result_str, err_str = process.communicate(str(input_doc))
if err_str:
if test_error_str:
self.assertIn(test_error_str, err_str)
return
if debug:
print('input :', self.canonicalize(input_doc))
print('result:', self.canonicalize(result_str))
print('expect:', self.canonicalize(expect_doc))
self.assertEqual(self.canonicalize(result_str),
self.canonicalize(expect_doc))
def raw_contains(self, phoenix_id, titanic_id, input_doc, expect_str,
test_error_str='', debug=False):
process = subprocess.Popen('python Gramps.py -d .ImportXML '
'--config=preferences.eprefix:DEFAULT '
'-i - -f gramps -a tool '
'-p "name=climerge,primary=%s,secondary=%s" '
'-e - -f raw' % (phoenix_id, titanic_id),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
result_str, err_str = process.communicate(str(input_doc))
if err_str:
if test_error_str:
self.assertIn(test_error_str, err_str)
return
if debug:
print('input :', self.canonicalize(input_doc))
print('result:', result_str)
print('expect:', expect_str)
# should I order/canonicalise things?
self.assertIn(expect_str, result_str)
#-------------------------------------------------------------------------
#
# 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>
</source>
</sources>
<places>
<placeobj handle="_p0000" id="P0000">
<ptitle>Place 0</ptitle>
</placeobj>
<placeobj handle="_p0001" id="P0001">
<ptitle>Place 1</ptitle>
</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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_event_merge(self):
"""Merge two events"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
eventref = ctxt.xpathEval(
"//g:person[@handle='_i0001']/g:eventref")[0]
eventref.setProp('hlink', '_e0000')
event = ctxt.xpathEval("//g:event[@handle='_e0001']")[0]
event.unlinkNode()
event.freeNode()
self.do_test('E0000', 'E0001', self.basedoc, expect)
def test_place_merge(self):
"""Merge two places"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
place = ctxt.xpathEval(
"//g:person[@handle='_i0001']/g:lds_ord/g:place")[0]
place.setProp('hlink', '_p0000')
placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0]
placeobj.unlinkNode()
placeobj.freeNode()
self.do_test('P0000', 'P0001', self.basedoc, expect)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
srcref = ctxt.xpathEval(
"//g:person[@handle='_i0001']/g:citationref")[0]
srcref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_media_merge(self):
"""Merge two media objects"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
objref = ctxt.xpathEval(
"//g:person[@handle='_i0001']/g:objref")[0]
objref.setProp('hlink', '_o0000')
object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0]
object_.unlinkNode()
object_.freeNode()
self.do_test('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:person[@handle='_i0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('N0000', 'N0001', 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">
<ptitle>Place 0</ptitle>
</placeobj>
<placeobj handle="_p0001" id="P0001">
<ptitle>Place 1</ptitle>
</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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_event_merge(self):
"""Merge two events"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
eventref = ctxt.xpathEval(
"//g:family[@handle='_f0001']/g:eventref")[0]
eventref.setProp('hlink', '_e0000')
event = ctxt.xpathEval("//g:event[@handle='_e0001']")[0]
event.unlinkNode()
event.freeNode()
self.do_test('E0000', 'E0001', self.basedoc, expect)
def test_place_merge(self):
"""Merge two places"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
place = ctxt.xpathEval(
"//g:family[@handle='_f0001']/g:lds_ord/g:place")[0]
place.setProp('hlink', '_p0000')
placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0]
placeobj.unlinkNode()
placeobj.freeNode()
self.do_test('P0000', 'P0001', self.basedoc, expect)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citref = ctxt.xpathEval(
"//g:family[@handle='_f0001']/g:citationref")[0]
citref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_media_merge(self):
"""Merge two media objects"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
objref = ctxt.xpathEval(
"//g:family[@handle='_f0001']/g:objref")[0]
objref.setProp('hlink', '_o0000')
object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0]
object_.unlinkNode()
object_.freeNode()
self.do_test('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:family[@handle='_f0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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">
<ptitle>Place 0</ptitle>
</placeobj>
<placeobj handle="_p0001" id="P0001">
<ptitle>Place 1</ptitle>
</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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_place_merge(self):
"""Merge two places"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
place = ctxt.xpathEval(
"//g:event[@handle='_e0001']/g:place")[0]
place.setProp('hlink', '_p0000')
placeobj = ctxt.xpathEval("//g:placeobj[@handle='_p0001']")[0]
placeobj.unlinkNode()
placeobj.freeNode()
self.do_test('P0000', 'P0001', self.basedoc, expect)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citref = ctxt.xpathEval(
"//g:event[@handle='_e0001']/g:citationref")[0]
citref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_media_merge(self):
"""Merge two media objects"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
objref = ctxt.xpathEval(
"//g:event[@handle='_e0001']/g:objref")[0]
objref.setProp('hlink', '_o0000')
object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0]
object_.unlinkNode()
object_.freeNode()
self.do_test('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:event[@handle='_e0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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">
<ptitle>Place 0</ptitle>
<objref hlink="_o0000"/>
<noteref hlink="_n0000"/>
<citationref hlink="_c0000"/>
</placeobj>
<placeobj handle="_p0001" id="P0001">
<ptitle>Place 1</ptitle>
<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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citref = ctxt.xpathEval(
"//g:placeobj[@handle='_p0001']/g:citationref")[0]
citref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_media_merge(self):
"""Merge two media objects"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
objref = ctxt.xpathEval(
"//g:placeobj[@handle='_p0001']/g:objref")[0]
objref.setProp('hlink', '_o0000')
object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0]
object_.unlinkNode()
object_.freeNode()
self.do_test('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:placeobj[@handle='_p0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
#def test_citation_merge(self): SEE special cases.
def test_repo_merge(self):
"""Merge two repositories"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
# Adjust the repository reference in expectation.
reporef = ctxt.xpathEval(
"//g:source[@handle='_s0001']/g:reporef")[0]
reporef.setProp('hlink', '_r0000')
# Remove one repository in expectation.
repo = ctxt.xpathEval("//g:repository[@handle='_r0001']")[0]
repo.unlinkNode()
repo.freeNode()
# Do the actual merger and comparison.
self.do_test('R0000', 'R0001', self.basedoc, expect)
def test_media_merge(self):
"""Merge two media objects"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
objref = ctxt.xpathEval(
"//g:source[@handle='_s0001']/g:objref")[0]
objref.setProp('hlink', '_o0000')
object_ = ctxt.xpathEval("//g:object[@handle='_o0001']")[0]
object_.unlinkNode()
object_.freeNode()
self.do_test('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:source[@handle='_s0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citref = ctxt.xpathEval(
"//g:repository[@handle='_r0001']/g:address/g:citationref")[0]
citref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:repository[@handle='_r0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_citation_merge(self):
"""Merge two citations"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citref = ctxt.xpathEval(
"//g:object[@handle='_o0001']/g:citationref")[0]
citref.setProp('hlink', '_c0000')
citation = ctxt.xpathEval("//g:citation[@handle='_c0001']")[0]
citation.unlinkNode()
citation.freeNode()
self.do_test('C0000', 'C0001', self.basedoc, expect)
def test_note_merge(self):
"""Merge two notes"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
noteref = ctxt.xpathEval(
"//g:object[@handle='_o0001']/g:noteref")[0]
noteref.setProp('hlink', '_n0000')
note = ctxt.xpathEval("//g:note[@handle='_n0001']")[0]
note.unlinkNode()
note.freeNode()
self.do_test('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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_citation_merge(self):
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citrefs = ctxt.xpathEval(
"//g:citation[@handle='_c0001']/g:objref/g:citationref")
citrefs[0].setProp('hlink', '_c0002')
citations = ctxt.xpathEval("//g:citation[@handle='_c0003']")
citations[0].unlinkNode()
citations[0].freeNode()
self.do_test('C0002', 'C0003', self.basedoc, expect)
def test_citation_cross_merge(self):
with XpathContext(self.basedoc) as input_ctxt:
input_citrefs = input_ctxt.xpathEval(
"//g:citation/g:objref/g:citationref")
input_citrefs[0].setProp('hlink', '_c0001')
input_citrefs[1].setProp('hlink', '_c0000')
rmcit = input_ctxt.xpathEval("//g:citation[@handle='_c0002']")
rmcit[0].unlinkNode()
rmcit[0].freeNode()
rmcit = input_ctxt.xpathEval("//g:citation[@handle='_c0003']")
rmcit[0].unlinkNode()
rmcit[0].freeNode()
rmsrc = input_ctxt.xpathEval("//g:source[@handle='_s0001']")
rmsrc[0].unlinkNode()
rmsrc[0].freeNode()
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
citrefs = ctxt.xpathEval(
"//g:citation[@handle='_c0000']/g:objref/g:citationref")
citrefs[0].setProp('hlink', '_c0000')
# add objref
objref = ctxt.xpathEval("//g:citation[@handle='_c0000']/g:objref")
objref2 = ctxt.xpathEval("//g:citation[@handle='_c0001']/g:objref")
objref[0].addNextSibling(objref2[0])
# remove citation
citations = ctxt.xpathEval("//g:citation[@handle='_c0001']")
citations[0].unlinkNode()
citations[0].freeNode()
self.do_test('C0000', 'C0001', self.basedoc, 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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
surname = Surname()
surname.set_surname("Person 0")
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
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
with XpathContext(self.basedoc) as ctxt:
events = ctxt.xpathEval("//g:events")
second_birth = events[0].newChild(None, 'event', None)
second_birth.newProp('handle', '_e0003')
second_birth.newProp('id', 'E0003')
second_birth.newChild(None, 'type', 'Birth')
second_birth.newChild(None, 'description', 'Event 3')
person = ctxt.xpathEval("//g:person[@handle='_i0000']")
eventref = person[0].newChild(None, 'eventref', None)
eventref.newProp('hlink', '_e0003')
eventref.newProp('role', 'Primary')
expect_str = self.expect_str + "1, 2"
self.raw_contains('E0000', 'E0001', self.basedoc, 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
with XpathContext(self.basedoc) as ctxt:
events = ctxt.xpathEval("//g:events")
second_death = events[0].newChild(None, 'event', None)
second_death.newProp('handle', '_e0003')
second_death.newProp('id', 'E0003')
second_death.newChild(None, 'type', 'Death')
second_death.newChild(None, 'description', 'Event 3')
person = ctxt.xpathEval("//g:person[@handle='_i0000']")
eventref = person[0].newChild(None, 'eventref', None)
eventref.newProp('hlink', '_e0003')
eventref.newProp('role', 'Primary')
expect_str = self.expect_str + "2, 1"
self.raw_contains('E0000', 'E0002', self.basedoc, 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>
</database>"""
self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_person_merge(self):
"""There is a person not involved in merger that references Titanic."""
with XpathContext(self.basedoc) as input_ctxt:
people = input_ctxt.xpathEval("//g:people")[0]
person = people.newChild(None, 'person', None)
person.newProp('handle', '_i0002')
person.newProp('id', 'I0002')
person.newChild(None, 'gender', 'M')
name = person.newChild(None, 'name', None)
name.newProp('type', 'Birth Name')
name.newChild(None, 'surname', 'Person 2')
personref = person.newChild(None, 'personref', None)
personref.newProp('hlink', '_i0001')
personref.newProp('rel', 'Neighbour')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
personref = ctxt.xpathEval("//g:personref")[0]
personref.setProp('hlink', '_i0000')
person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0]
altname = person.newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 1')
attr = person.newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0001')
person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0]
person.unlinkNode()
person.freeNode()
self.do_test('I0000', 'I0001', self.basedoc, expect)
def test_person_cross(self):
"""Phoenix has ref to Titanic and vice versa"""
with XpathContext(self.basedoc) as input_ctxt:
persons = input_ctxt.xpathEval("//g:person")
personref = persons[0].newChild(None, 'personref', None)
personref.newProp('hlink', '_i0001')
personref.newProp('rel','Neighbour East')
personref = persons[1].newChild(None, 'personref', None)
personref.newProp('hlink', '_i0000')
personref.newProp('rel','Neighbour West')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
personref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:personref")[0]
personref.setProp('hlink', '_i0000')
person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0]
altname = person.newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 1')
attr = person.newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0001')
attr.addNextSibling(personref) # restore order of elements
personref = person.newChild(None, 'personref', None)
personref.newProp('hlink', '_i0000')
personref.newProp('rel', 'Neighbour West')
person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0]
person.unlinkNode()
person.freeNode()
self.do_test('I0000', 'I0001', self.basedoc, expect)
def test_person_self(self):
"""Titanic references itself"""
with XpathContext(self.basedoc) as input_ctxt:
person = input_ctxt.xpathEval("//g:person[@handle='_i0001']")[0]
personref = person.newChild(None, 'personref', None)
personref.newProp('hlink', '_i0001')
personref.newProp('rel', 'Neighbour')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
person = ctxt.xpathEval("//g:person[@handle='_i0000']")[0]
altname = person.newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 1')
attr = person.newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0001')
personref = person.newChild(None, 'personref', None)
personref.newProp('hlink', '_i0000')
personref.newProp('rel', 'Neighbour')
person = ctxt.xpathEval("//g:person[@handle='_i0001']")[0]
person.unlinkNode()
person.freeNode()
self.do_test('I0000', 'I0001', self.basedoc, 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 = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_person_merge(self):
"""Merge two persons that are children in some family"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
childref = ctxt.xpathEval("//g:family[@handle='_f0001']/g:childref")[0]
childref.setProp('hlink', '_i0000')
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 1')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0001')
childof = ctxt.xpathEval("//g:person[@handle='_i0000']/g:childof")[0]
attr.addNextSibling(childof) # restore order of elements
childof = persons[0].newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
persons[1].unlinkNode()
persons[1].freeNode()
self.do_test('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>
</database>"""
self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_titanic_no_fam(self):
"Test merge of two persons where titanic is not a parent in a family"
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
self.do_test('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."""
with XpathContext(self.basedoc) as input_ctxt:
person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0]
parentin = person.newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
parentin = persons[0].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
father = ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0]
father.setProp('hlink', '_i0000')
persons[2].unlinkNode()
persons[2].freeNode()
self.do_test('I0000', 'I0002', self.basedoc, expect)
def test_multi_rel(self):
"""Merge two persons where titanic has multiple family relationships
with his partner, this should raise an error."""
with XpathContext(self.basedoc) as input_ctxt:
persons = input_ctxt.xpathEval("//g:person")
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
parentin = persons[3].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0002')
parentin = persons[3].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0002')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
mother = family.newChild(None, 'mother', None)
mother.newProp('hlink', '_i0003')
families = input_ctxt.xpathEval("//g:families")[0]
family = families.newChild(None, 'family', None)
family.newProp('handle', '_f0002')
family.newProp('id', 'F0002')
rel = family.newChild(None, 'rel', None)
rel.newProp('type', 'Married')
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
mother = family.newChild(None, 'mother', None)
mother.newProp('hlink', '_i0003')
with CopiedDoc(self.basedoc) as expect:
self.do_test('I0000', 'I0002', self.basedoc, expect,
test_error_str=_("A person with multiple relations "
"with the same spouse is about to be merged. This is "
"beyond the capabilities of the merge routine. The "
"merge is aborted."))
def test_merge_fam(self):
"""Merge two persons such that also the families in which they are
parents get merged."""
with XpathContext(self.basedoc) as input_ctxt:
persons = input_ctxt.xpathEval("//g:person")
parentin = persons[1].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
mother = family.newChild(None, 'mother', None)
mother.newProp('hlink', '_i0001')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
parentin = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[1]
parentin.unlinkNode()
parentin.freeNode()
self.do_test('I0000', 'I0002', self.basedoc, expect)
def test_fam_none_merge(self):
"""Merge two persons, both father in families without mothers."""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0]
parentin = person.newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
self.do_test('I0000', 'I0002', self.basedoc, 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."""
with XpathContext(self.basedoc) as input_ctxt:
persons = input_ctxt.xpathEval("//g:person")
parentin = persons[0].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
parentin = persons[3].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0000')
mother = family.newChild(None, 'mother', None)
mother.newProp('hlink', '_i0003')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
parentin = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[1]
parentin.unlinkNode()
parentin.freeNode()
self.do_test('I0001', 'I0003', self.basedoc, expect)
def test_childref_notyet(self):
"""Merge two people leading to merger of families that have children."""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
persons = input_ctxt.xpathEval("//g:person")
childof = persons[1].newChild(None, 'childof', None)
childof.newProp('hlink', '_f0000')
families = input_ctxt.xpathEval("//g:family")
childref = families[0].newChild(None, 'childref', None)
childref.newProp('hlink', '_i0001')
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
father = families[1].newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
childof = persons[3].newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
childref = families[1].newChild(None, 'childref', None)
childref.newProp('hlink', '_i0003')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
families = ctxt.xpathEval("//g:family")
families[1].unlinkNode()
families[1].freeNode()
childof = ctxt.xpathEval("//g:person[@handle='_i0003']/g:childof")[0]
childof.setProp('hlink', '_f0000')
childref = families[0].newChild(None, 'childref', None)
childref.newProp('hlink', '_i0003')
self.do_test('I0000', 'I0002', self.basedoc, expect)
def test_childref_already(self):
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
persons = input_ctxt.xpathEval("//g:person")
childof = persons[1].newChild(None, 'childof', None)
childof.newProp('hlink', '_f0000')
childof = persons[1].newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
families = input_ctxt.xpathEval("//g:family")
childref = families[0].newChild(None, 'childref', None)
childref.newProp('hlink', '_i0001')
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
father = families[1].newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
childref = families[1].newChild(None, 'childref', None)
childref.newProp('hlink', '_i0001')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
families = ctxt.xpathEval("//g:family")
families[1].unlinkNode()
families[1].freeNode()
childof = ctxt.xpathEval("//g:person[@handle='_i0001']/g:childof")
childof[1].unlinkNode()
childof[1].freeNode()
self.do_test('I0000', 'I0002', self.basedoc, expect)
def test_ldsord(self):
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
persons = input_ctxt.xpathEval("//g:person")
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
ldsord = persons[3].newChild(None, 'lds_ord', None)
ldsord.newProp('type', 'sealed_to_parents')
sealedto = ldsord.newChild(None, 'sealed_to', None)
sealedto.newProp('hlink', '_f0001')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
families = ctxt.xpathEval("//g:family")
families[1].unlinkNode()
families[1].freeNode()
sealedto = ctxt.xpathEval("//g:sealed_to")[0]
sealedto.setProp('hlink', '_f0000')
self.do_test('I0000', 'I0002', self.basedoc, expect)
# This test fails because the assigment of family ids shifts F0000 to F0001
# and F0001 to F0002!
#def test_ldsord_cross(self):
# with XpathContext(self.basedoc) as input_ctxt:
# parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
# parentin.unlinkNode()
# parentin.freeNode()
# mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
# mother.unlinkNode()
# mother.freeNode()
# persons = input_ctxt.xpathEval("//g:person")
# parentin = persons[2].newChild(None, 'parentin', None)
# parentin.newProp('hlink', '_f0001')
# family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
# father = family.newChild(None, 'father', None)
# father.newProp('hlink', '_i0002')
# ldsord = persons[0].newChild(None, 'lds_ord', None)
# ldsord.newProp('type', 'sealed_to_parents')
# sealedto = ldsord.newChild(None, 'sealed_to', None)
# sealedto.newProp('hlink', '_f0001')
# with CopiedDoc(self.basedoc) as expect:
# with XpathContext(expect) as ctxt:
# persons = ctxt.xpathEval("//g:person")
# altname = persons[0].newChild(None, 'name', None)
# altname.newProp('alt', '1')
# altname.newProp('type', 'Birth Name')
# altname.newChild(None, 'surname', 'Person 2')
# ldsord = ctxt.xpathEval("//g:lds_ord")[0]
# altname.addNextSibling(ldsord) # restore order of elements
# attr = persons[0].newChild(None, 'attribute', None)
# attr.newProp('type', 'Merged Gramps ID')
# attr.newProp('value', 'I0002')
# parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
# attr.addNextSibling(parentref) # restore order of elements
# persons[2].unlinkNode()
# persons[2].freeNode()
# families = ctxt.xpathEval("//g:family")
# families[1].unlinkNode()
# families[1].freeNode()
# sealedto = ctxt.xpathEval("//g:sealed_to")[0]
# sealedto.setProp('hlink', '_f0000')
# self.do_test('I0000', 'I0002', self.basedoc, expect)
def test_ldsord_self(self):
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
persons = input_ctxt.xpathEval("//g:person")
parentin = persons[2].newChild(None, 'parentin', None)
parentin.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
father = family.newChild(None, 'father', None)
father.newProp('hlink', '_i0002')
ldsord = persons[2].newChild(None, 'lds_ord', None)
ldsord.newProp('type', 'sealed_to_parents')
sealedto = ldsord.newChild(None, 'sealed_to', None)
sealedto.newProp('hlink', '_f0001')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
ldsord = ctxt.xpathEval("//g:lds_ord")[0]
altname.addNextSibling(ldsord) # restore order of elements
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
families = ctxt.xpathEval("//g:family")
families[1].unlinkNode()
families[1].freeNode()
sealedto = ctxt.xpathEval("//g:sealed_to")[0]
sealedto.setProp('hlink', '_f0000')
self.do_test('I0000', 'I0002', self.basedoc, 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>
</database>"""
self.basedoc = libxml2.readDoc(self.base_str + base_str, '', None,
libxml2.XML_PARSE_NONET)
def test_father_son_merge(self):
"""Merge two families where the fathers have a father-son relationship
so that an error is raised."""
with XpathContext(self.basedoc) as input_ctxt:
person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0]
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0000')
family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0002')
with CopiedDoc(self.basedoc) as expect:
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, 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."""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
father = input_ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0]
father.unlinkNode()
father.freeNode()
person = input_ctxt.xpathEval("//g:person[@handle='_i0000']")[0]
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0000')
with CopiedDoc(self.basedoc) as expect:
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, 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."""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
father = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:father")[0]
father.unlinkNode()
father.freeNode()
person = input_ctxt.xpathEval("//g:person[@handle='_i0002']")[0]
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0000')
family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0002')
with CopiedDoc(self.basedoc) as expect:
self.do_family_test('F0000', 'F0001', 'i0002', 'i0001',
self.basedoc, 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"""
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001', self.basedoc, expect)
def test_father_swapped(self):
"Merge two families where the phoenix-father is of the titanic family."
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[2].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 0')
attr = persons[2].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0000')
parentref = ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0]
parentref.setProp('hlink', '_f0000')
attr.addNextSibling(parentref) # restore order of elements
persons[0].unlinkNode()
persons[0].freeNode()
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
father = ctxt.xpathEval("//g:family[@handle='_f0000']/g:father")[0]
father.setProp('hlink', '_i0002')
self.do_family_test('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"""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0002']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
father = input_ctxt.xpathEval("//g:family[@handle='_f0001']/g:father")[0]
father.unlinkNode()
father.freeNode()
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, 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."""
with XpathContext(self.basedoc) as input_ctxt:
parentin = input_ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
parentin.unlinkNode()
parentin.freeNode()
mother = input_ctxt.xpathEval("//g:family[@handle='_f0000']/g:mother")[0]
mother.unlinkNode()
mother.freeNode()
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
parentin = ctxt.xpathEval("//g:person[@handle='_i0003']/g:parentin")[0]
parentin.setProp('hlink', '_f0000')
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0000']")[0]
mother = family.newChild(None, 'mother', None)
mother.newProp('hlink', '_i0003')
self.do_family_test('F0000', 'F0001', 'i0000', 'i0003',
self.basedoc, expect)
#def test_no_parents(self):
def test_childref_notyet(self):
"""Merge two families with non-duplicate child references."""
with XpathContext(self.basedoc) as input_ctxt:
people = input_ctxt.xpathEval("//g:people")[0]
person = people.newChild(None, 'person', None)
person.newProp('handle', '_i0004')
person.newProp('id', '_I0004')
person.newChild(None, 'gender', 'M')
name = person.newChild(None, 'name', None)
name.newProp('type', 'Birth Name')
name.newChild(None, 'surname', 'Person 4')
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0004')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
childof = ctxt.xpathEval("//g:person[@handle='_i0004']/g:childof")[0]
childof.setProp('hlink', '_f0000')
family = ctxt.xpathEval("//g:family[@handle='_f0000']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0004')
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, expect)
def test_childref_already(self):
"""Merge two families with duplicate child references."""
with XpathContext(self.basedoc) as input_ctxt:
people = input_ctxt.xpathEval("//g:people")[0]
person = people.newChild(None, 'person', None)
person.newProp('handle', '_i0004')
person.newProp('id', '_I0004')
person.newChild(None, 'gender', 'M')
name = person.newChild(None, 'name', None)
name.newProp('type', 'Birth Name')
name.newChild(None, 'surname', 'Person 4')
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0000')
childof = person.newChild(None, 'childof', None)
childof.newProp('hlink', '_f0001')
family = input_ctxt.xpathEval("//g:family[@handle='_f0000']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0004')
family = input_ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
childref = family.newChild(None, 'childref', None)
childref.newProp('hlink', '_i0004')
with CopiedDoc(self.basedoc) as expect:
with XpathContext(expect) as ctxt:
persons = ctxt.xpathEval("//g:person")
altname = persons[0].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 2')
attr = persons[0].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0002')
parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[2].unlinkNode()
persons[2].freeNode()
altname = persons[1].newChild(None, 'name', None)
altname.newProp('alt', '1')
altname.newProp('type', 'Birth Name')
altname.newChild(None, 'surname', 'Person 3')
attr = persons[1].newChild(None, 'attribute', None)
attr.newProp('type', 'Merged Gramps ID')
attr.newProp('value', 'I0003')
parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
attr.addNextSibling(parentref) # restore order of elements
persons[3].unlinkNode()
persons[3].freeNode()
family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
family.unlinkNode()
family.freeNode()
childof = ctxt.xpathEval("//g:person[@handle='_i0004']/g:childof")[1]
childof.unlinkNode()
childof.freeNode()
self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, 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."""
# with XpathContext(self.basedoc) as input_ctxt:
# person = input_ctxt.xpathEval("//g:person[@handle='_i0000']")[0]
# ldsord = person.newChild(None, 'lds_ord', None)
# ldsord.newProp('type', 'sealed_to_parents')
# sealedto = ldsord.newChild(None, 'sealed_to', None)
# sealedto.newProp('hlink', '_f0001')
# parentin = input_ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
# ldsord.addNextSibling(parentin)
# with CopiedDoc(self.basedoc) as expect:
# with XpathContext(expect) as ctxt:
# persons = ctxt.xpathEval("//g:person")
# altname = persons[0].newChild(None, 'name', None)
# altname.newProp('alt', '1')
# altname.newProp('type', 'Birth Name')
# altname.newChild(None, 'surname', 'Person 2')
# ldsord = ctxt.xpathEval("//g:lds_ord")[0]
# altname.addNextSibling(ldsord) # restore order of elements
# attr = persons[0].newChild(None, 'attribute', None)
# attr.newProp('type', 'Merged Gramps ID')
# attr.newProp('value', 'I0002')
# parentref = ctxt.xpathEval("//g:person[@handle='_i0000']/g:parentin")[0]
# attr.addNextSibling(parentref) # restore order of elements
# persons[2].unlinkNode()
# persons[2].freeNode()
# altname = persons[1].newChild(None, 'name', None)
# altname.newProp('alt', '1')
# altname.newProp('type', 'Birth Name')
# altname.newChild(None, 'surname', 'Person 3')
# attr = persons[1].newChild(None, 'attribute', None)
# attr.newProp('type', 'Merged Gramps ID')
# attr.newProp('value', 'I0003')
# parentref = ctxt.xpathEval("//g:person[@handle='_i0001']/g:parentin")[0]
# attr.addNextSibling(parentref) # restore order of elements
# persons[3].unlinkNode()
# persons[3].freeNode()
# family = ctxt.xpathEval("//g:family[@handle='_f0001']")[0]
# family.unlinkNode()
# family.freeNode()
# sealedto = ctxt.xpathEval("//g:sealed_to")[0]
# sealedto.setProp('hlink', '_f0000')
# self.do_family_test('F0000', 'F0001', 'i0000', 'i0001',
# self.basedoc, 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()