diff --git a/capture_gedcom_warnings.py b/capture_gedcom_warnings.py
new file mode 100644
index 00000000000..7620b83b752
--- /dev/null
+++ b/capture_gedcom_warnings.py
@@ -0,0 +1,103 @@
+#! /usr/bin/env python3
+"""Capture the GEDCOM import warning text without the GUI.
+
+Replicates the import path used by Gramps (importData -> GedcomParser ->
+parse_gedcom_file -> user.info(..., "".join(errors), ...)) but with a
+User subclass that records the info text instead of showing a dialog.
+
+Usage:
+ python capture_gedcom_warnings.py [input_file] [output_file]
+
+Examples:
+ python capture_gedcom_warnings.py
+ python capture_gedcom_warnings.py imp_notetest_dfs.ged
+ python capture_gedcom_warnings.py imp_notetest_dfs.ged report.txt
+"""
+
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2026 Gramps Development Team
+#
+# 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, see .
+#
+
+# -------------------------------------------------------------------------
+#
+# Standard Python modules
+#
+# -------------------------------------------------------------------------
+import os
+import sys
+
+# -------------------------------------------------------------------------
+#
+# Gramps modules
+#
+# -------------------------------------------------------------------------
+from gramps.gen.const import TEST_DIR
+from gramps.gen.db.utils import make_database
+from gramps.gen.user import User
+from gramps.gen.utils.config import config
+from gramps.plugins.importer.importgedcom import importData
+
+
+class CaptureUser(User):
+ """A User that captures the info() text instead of displaying it."""
+
+ def __init__(self):
+ super().__init__()
+ self.report_title = ""
+ self.infotext = ""
+
+ def info(self, msg1, infotext, parent=None, monospaced=False):
+ self.report_title = msg1
+ self.infotext = infotext
+
+
+def main():
+ """Import the GEDCOM and print the captured warning text."""
+ input_file = (
+ sys.argv[1] if len(sys.argv) > 1 else "imp_notetest_lds_in-out-in_dfs.ged"
+ )
+ output_file = sys.argv[2] if len(sys.argv) > 2 else None
+ fn1 = os.path.join(TEST_DIR, input_file)
+
+ # Same "_dfs" preferences as imports_test.py make_tst_function()
+ config.set("preferences.default-source", True)
+ config.set("preferences.tag-on-import-format", "Imported")
+ config.set("preferences.tag-on-import", True)
+
+ # Set up a fresh in-memory database like the test does.
+ db = make_database("sqlite")
+ db.load(":memory:")
+ db.set_feature("skip-import-additions", False)
+
+ user = CaptureUser()
+ importData(db, fn1, user)
+
+ print("REPORT TITLE:", user.report_title)
+ print("REPORT TEXT:")
+ print(user.infotext)
+
+ if output_file:
+ with open(output_file, "w", encoding="utf-8") as out:
+ out.write(user.report_title + "\n")
+ out.write(user.infotext)
+ print(f"Wrote {output_file}")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/data/tests/imp_FTM_16dec2015a-mod1.gramps b/data/tests/imp_FTM_16dec2015a-mod1.gramps
index 93f391ed7c1..5a128f7eee2 100644
--- a/data/tests/imp_FTM_16dec2015a-mod1.gramps
+++ b/data/tests/imp_FTM_16dec2015a-mod1.gramps
@@ -1,9 +1,9 @@
-
-
+
+
-
+
@@ -147,19 +147,19 @@
Records not imported into OBJE (multi-media object) Gramps ID M159:
-Could not import 1850 United States Federal Census(11)-1.jpg Line 70: 1 FILE 1850 United States Federal Census(11)-1.jpg
+Could not import 1850 United States Federal Census(11)-1.jpg Line 70: 1 FILE 1850 United States Federal Census(11)-1.jpg
Records not imported into OBJE (multi-media object) Gramps ID M158:
-Could not import D:\Users\PRC\Downloads\1850 United States Federa Line 75: 1 FILE D:\Users\PRC\Downloads\1850 United States Federal Census(11)-1.jpg
+Could not import D:\Users\PRC\Downloads\1850 United States Federal Census(11)-1.jpg Line 75: 1 FILE D:\Users\PRC\Downloads\1850 United States Federal Census(11)-1.jpg
diff --git a/data/tests/imp_FTM_PHOTO.gramps b/data/tests/imp_FTM_PHOTO.gramps
index ec144dc5ae6..2d82b90b3c4 100644
--- a/data/tests/imp_FTM_PHOTO.gramps
+++ b/data/tests/imp_FTM_PHOTO.gramps
@@ -1,14 +1,14 @@
-
-
+
+
-
+
-
+ UThe
@@ -21,7 +21,7 @@
-
+ UShe
@@ -35,7 +35,7 @@
-
+ UEdwin
@@ -49,102 +49,109 @@
-
-
+ The primary photo should be a male Sourpuss in a Hat 03.jpg or Gid:M3
-
+ The primary photo should be a girl eating 04.jpg or Gid:M4
-
+ Records not imported into INDI (individual) Gramps ID I0002:
-Line ignored as not understood Line 35: 3 WHAT ???
+Line ignored as not understood Line 35: 3 WHAT ???
+
-
+ Records not imported into OBJE (multi-media object) Gramps ID M1:
-Could not import O1.jpg Line 38: 1 FILE O1.jpg
+Could not import O1.jpg Line 38: 1 FILE O1.jpg
+
-
+ Records not imported into OBJE (multi-media object) Gramps ID M2:
-Could not import O2.jpg Line 41: 1 FILE O2.jpg
+Could not import O2.jpg Line 41: 1 FILE O2.jpg
+
-
+ Records not imported into OBJE (multi-media object) Gramps ID M3:
-Could not import O3.jpg Line 44: 1 FILE O3.jpg
+Could not import O3.jpg Line 44: 1 FILE O3.jpg
+
-
+ Records not imported into OBJE (multi-media object) Gramps ID M4:
-Could not import O4.jpg Line 47: 1 FILE O4.jpg
+Could not import O4.jpg Line 47: 1 FILE O4.jpg
+
-
+ Records not imported into OBJE (multi-media object) Gramps ID M5:
-Could not import O5.jpg Line 50: 1 FILE O5.jpg
+Could not import O5.jpg Line 50: 1 FILE O5.jpg
+
-
+ The primary photo should be a male of pair 05.jpg
-
+ Records not imported into INDI (individual) Gramps ID I0003:
-Could not import O3.jpg Line 55: 1 OBJE
-Could not import O4.jpg Line 59: 1 OBJE
-Could not import O5.jpg Line 63: 1 OBJE
+Could not import O3.jpg Line 55: 1 OBJE
+Could not import O4.jpg Line 59: 1 OBJE
+Could not import O5.jpg Line 63: 1 OBJE
+
diff --git a/data/tests/imp_MediaTest.gramps b/data/tests/imp_MediaTest.gramps
index 86decb3568c..1b26571deb6 100644
--- a/data/tests/imp_MediaTest.gramps
+++ b/data/tests/imp_MediaTest.gramps
@@ -1,9 +1,9 @@
-
-
+
+
-
+
@@ -68,7 +68,7 @@
-
+
@@ -77,12 +77,12 @@
-
+
-
+
@@ -106,7 +106,7 @@
-
+
@@ -161,12 +161,12 @@ n OBJE
Records not imported into INDI (individual) Gramps ID I0001:
-Could not import test_emb_55.jpg Line 18: 1 OBJE
-Could not import test_emb_551.jpg Line 26: 1 OBJE
-Could not import test_emb_55.jpg Line 34: 1 OBJE
+Could not import test_emb_55.jpg Line 18: 1 OBJE
+Could not import test_emb_551.jpg Line 26: 1 OBJE
+Could not import test_emb_55.jpg Line 34: 1 OBJE
@@ -189,30 +189,32 @@ n @XREF:OBJE@ OBJE {1:1}
Records not imported into OBJE (multi-media object) Gramps ID M1:
-Tag recognized but not supported Line 58: 1 BLOB
- .HM.......k.1..F.jwA.Dzzzzw............A....1.........0U.66..E.8
- .......A..k.a6.A.......A..k.........../6....G.......0../..U.....
- .w1/m........HC0..../...zzzzzzzz..5zzk..AnA..U..W6U....2rRrRrRrR
- .Dw...............k.1.......1..A...5ykE/zzzx/.g//.Hxzk6/.Tzy/.k1
- /Dw/.Tvz.E5zzUE9/kHz.Tw2/DzzzEEA.kE2zk5yzk2/zzs21.U2/Dw/.Tw/.Tzy
- /.fy/.HzzkHzzzo21Ds00.E2.UE2.U62/.k./Ds0.UE0/Do0..E8/UE2.U62.U9w
- /.Tx/.20.jg2/jo2..9u/.0U.6A.zk
-Line ignored as not understood Line 66: 1 OBJE @M2@
-Filename omitted Line 55: 0 M1 OBJE
+Tag recognized but not supported: '
+.HM.......k.1..F.jwA.Dzzzzw............A....1.........0U.66..E. Line 58: 1 BLOB
+ .HM.......k.1..F.jwA.Dzzzzw............A....1.........0U.66..E.8
+ .......A..k.a6.A.......A..k.........../6....G.......0../..U.....
+ .w1/m........HC0..../...zzzzzzzz..5zzk..AnA..U..W6U....2rRrRrRrR
+ .Dw...............k.1.......1..A...5ykE/zzzx/.g//.Hxzk6/.Tzy/.k1
+ /Dw/.Tvz.E5zzUE9/kHz.Tw2/DzzzEEA.kE2zk5yzk2/zzs21.U2/Dw/.Tw/.Tzy
+ /.fy/.HzzkHzzzo21Ds00.E2.UE2.U62/.k./Ds0.UE0/Do0..E8/UE2.U62.U9w
+ /.Tx/.20.jg2/jo2..9u/.0U.6A.zk
+Line ignored as not understood Line 66: 1 OBJE @M2@
+Filename omitted Line 55: 0 M1 OBJE
Records not imported into OBJE (multi-media object) Gramps ID M2:
-Tag recognized but not supported Line 77: 1 BLOB
- 67890gramps doesn't do this anyway, so don't bother doing it right.
-Filename omitted Line 74: 0 M2 OBJE
+Tag recognized but not supported: '
+67890gramps doesn't do this anyway, so don't bother doing it ri Line 77: 1 BLOB
+ 67890gramps doesn't do this anyway, so don't bother doing it right.
+Filename omitted Line 74: 0 M2 OBJE
@@ -226,10 +228,10 @@ n @XREF:OBJE@ OBJE {1:1}
Records not imported into OBJE (multi-media object) Gramps ID M3:
-Could not import test.jpg Line 82: 1 FILE test.jpg
+Could not import test.jpg Line 82: 1 FILE test.jpg
@@ -255,10 +257,10 @@ n @XREF:OBJE@ OBJE {1:1}
Records not imported into OBJE (multi-media object) Gramps ID M4:
-Could not import test.jpg Line 88: 1 FILE test.jpg
+Could not import test.jpg Line 88: 1 FILE test.jpg
@@ -280,10 +282,10 @@ n @XREF:OBJE@ OBJE {1:1}
Records not imported into OBJE (multi-media object) Gramps ID M5:
-Could not import test.jpg Line 103: 1 FILE test.jpg
+Could not import test.jpg Line 103: 1 FILE test.jpg
@@ -298,23 +300,23 @@ Could not import test.jpg Line 103:
Records not imported into OBJE (multi-media object) Gramps ID M6:
-Could not import test.jpg Line 111: 1 FILE test.jpg
-Multiple FILE in a single OBJE ignored Line 115: 1 FILE test1.jpg
-Skipped subordinate line Line 116: 2 FORM jpeg
-Skipped subordinate line Line 117: 3 TYPE photo
-Skipped subordinate line Line 118: 2 TITL Multimedia link to linked form v5.5.1 with two files(2)
+Could not import test.jpg Line 111: 1 FILE test.jpg
+Multiple FILE in a single OBJE ignored Line 115: 1 FILE test1.jpg
+Skipped subordinate line Line 116: 2 FORM jpeg
+Skipped subordinate line Line 117: 3 TYPE photo
+Skipped subordinate line Line 118: 2 TITL Multimedia link to linked form v5.5.1 with two files(2)
Records not imported into OBJE (multi-media object) Gramps ID M8:
-Could not import No_path_No_Title_NoForm.jpg Line 138: 1 FILE No_path_No_Title_NoForm.jpg
+Could not import No_path_No_Title_NoForm.jpg Line 138: 1 FILE No_path_No_Title_NoForm.jpg
diff --git a/data/tests/imp_PhonFax_dfs.gramps b/data/tests/imp_PhonFax_dfs.gramps
index a8d551f04e5..ea51462dc41 100644
--- a/data/tests/imp_PhonFax_dfs.gramps
+++ b/data/tests/imp_PhonFax_dfs.gramps
@@ -1,9 +1,9 @@
-
-
+
+
-
+ The Subm /Tester/123 Main St.
@@ -204,21 +204,21 @@
Records not imported into HEAD (header):
-Only one phone number supported Line 9: 3 PHON (800) 705-7000
-Line ignored as not understood Line 15: 2 DATE 1111-01-01
-GEDCOM FORM should be in uppercase Line 26: 2 FORM Lineage-Linked
+Only one phone number supported Line 9: 3 PHON (800) 705-7000
+Line ignored as not understood Line 15: 2 DATE 1111-01-01
+GEDCOM FORM should be in uppercase Line 26: 2 FORM Lineage-Linked
Records not imported into SUBM (Submitter): (@SUBM@) The Subm /Tester/:
-Only one phone number supported Line 35: 1 PHON 800-871-3401
+Only one phone number supported Line 35: 1 PHON 800-871-3401
@@ -236,10 +236,10 @@ Only one phone number supported Line 35:
Records not imported into REPO (repository) Gramps ID R0002:
-Only one phone number supported Line 93: 1 PHON 800-765-4321
+Only one phone number supported Line 93: 1 PHON 800-765-4321
diff --git a/data/tests/imp_bug_8322_test.gramps b/data/tests/imp_bug_8322_test.gramps
index 3a0edbd355f..808a1728497 100644
--- a/data/tests/imp_bug_8322_test.gramps
+++ b/data/tests/imp_bug_8322_test.gramps
@@ -1,171 +1,171 @@
-
-
+
+
-
+
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ Residence
-
+ MLiving
@@ -206,76 +206,76 @@
-
+ the place
-
+ the address
-
+ the address
-
+ another address
-
+ another address
-
+ the place
-
+ the address
-
+ the address
-
+ second address
-
+ the place 2
-
+ second address
-
+ a third address ignored again
-
+ place test
@@ -284,209 +284,210 @@
-
+ address place test
-
+ different place test
-
+ address place test
-
+ a new place
-
+ address place test
-
+ plus an address just for good measure also ignored
-
+ address with no place
-
+ Woerden, Zuid-Holland, Netherlands
-
+ Kromwijkerkade 63
-
+ Hasselt, Overijssel, Netherlands
-
+ Prinsenstraat 69
-
+ Enschede, Overijssel, Netherlands
-
+ Calslaan 26-52
-
+ Calslaan 26-44
-
+ Enschede, Overijssel, Netherlands
-
+ Calslaan 26-61
-
+ Amsterdam, Noord-Holland, Netherlands
-
+ Papendrechtstraat 37
-
+ Olympiaplein 46-2
-
+ Papendrechtstraat 37
-
+ Amsterdam, Noord-Holland, Netherlands
-
+ the place created not previously used so changed to add the address; __event_addr(len==0, place is None)
-
+ check that this note is retained when the place is deleted. it should be merged into place
-
+ the place created and then deleted and old data reused; __event_addr(len==0, place is not None)
-
+ setup the place
-
+ the place already exists; but now set doesn't match ; __event_addr(len!=0, place is None)
-
+ the place already exists but now set matches; __event_addr(len!=0, place is not None)
-
+ the address created, then destroyed as we find a matching set; __event_addr(no place_handle, create place)
-
+ address reused, then destroyed as we find a matching set; __event_addr(no place_handle, place found)
-
+ second address ignored
-
+ second address ignored again
-
+ ADDR created; __event_place finds it but place does not match; __event_place(len==0, place is None)
-
+ this note is stored with the old address and then merged into the matching place
-
+ ADDR created; __event_place finds it and now place does match; __event_place(len==0, place is not None)
-
+ setup address place test
-
+ address place test found; place exists and can be reused; __event_place(len!=0, place is not None)
-
+ address place test found; but matching addr/plac not found; __event_place(len!=0, place is not None)
-
+ PLAC occurs first; matching entry found; __event_place(no place handle, place is not None)
-
+ PLAC occurs first; matching entry not found; __event_place(no place handle, place is None)
-
+ note is stashed with a Place, and then merged into the address
-
+ Place note
-
+ ADDR note
-
+ PLAC previously encountered, new ADDR, so new Place
-
+ ADDR before PLAC (check ADDR is removed)
-
+ ADDR before PLAC (address matches previous one, then needs to be reassigned)
-
+ PLAC and no ADDR
-
+ PLAC matches previous one, then when ADDR is read need to create a new Place
-
+ PLAC and ADDR match, use existing one
-
+ Records not imported into INDI (individual) Gramps ID I0310:
-A second PLAC ignored Line 109: 2 PLAC a second PLACe ignored
-A second PLAC ignored Line 110: 2 PLAC and a third one also ignored
+A second PLAC ignored Line 109: 2 PLAC a second PLACe ignored
+A second PLAC ignored Line 110: 2 PLAC and a third one also ignored
+
diff --git a/data/tests/imp_notetest_dfs.gramps b/data/tests/imp_notetest_dfs.gramps
index 40d1d966e4d..70d2b405f96 100644
--- a/data/tests/imp_notetest_dfs.gramps
+++ b/data/tests/imp_notetest_dfs.gramps
@@ -1,9 +1,9 @@
-
-
+
+
-
+ John A. Tester
@@ -48,7 +48,7 @@
-
+ MTom
@@ -225,7 +225,7 @@
-
+
@@ -266,44 +266,44 @@
Records not imported into HEAD (header):
-Line ignored as not understood Line 18: 2 TEST Header Note
-Empty note ignored Line 19: 1 NOTE
-Skipped subordinate line Line 20: 2 TEST Empty Note
+Line ignored as not understood Line 18: 2 TEST Header Note
+Empty note ignored Line 19: 1 NOTE
+Skipped subordinate line Line 20: 2 TEST Empty Note
Submission Note
-
+ Submission xref noteRecords not imported into Top Level:
-Line ignored as not understood Line 26: 2 TEST Submission Note
-Empty note ignored Line 27: 1 NOTE
-Skipped subordinate line Line 28: 2 TEST Empty Note
-Line ignored as not understood Line 29: 1 TEST submission
+Line ignored as not understood Line 26: 2 TEST Submission Note
+Empty note ignored Line 27: 1 NOTE
+Skipped subordinate line Line 28: 2 TEST Empty Note
+Line ignored as not understood Line 29: 1 TEST submission
Records not imported into NOTE Gramps ID N0003:
-Tag recognized but not supported Line 32: 1 RIN Submission Note RIN
-Tag recognized but not supported Line 33: 1 REFN Submission Note REFN
-Skipped subordinate line Line 34: 2 TYPE Submission Note REFN TYPE
-Tag recognized but not supported Line 35: 1 SOUR Submission note source
-Line ignored as not understood Line 39: 1 TEST on XREF Note
+Tag recognized but not supported: 'Submission Note RIN' on line Line 32: 1 RIN Submission Note RIN
+Tag recognized but not supported: 'Submission Note REFN' on line Line 33: 1 REFN Submission Note REFN
+Skipped subordinate line Line 34: 2 TYPE Submission Note REFN TYPE
+Tag recognized but not supported: 'Submission note source' on line Line 35: 1 SOUR Submission note source
+Line ignored as not understood Line 39: 1 TEST on XREF Note
@@ -316,13 +316,13 @@ Line ignored as not understood Line 39:
Records not imported into SUBM (Submitter): (@SUBMITTER@) John A. Tester:
-Line ignored as not understood Line 43: 2 TEST Submitter Note
-Empty note ignored Line 44: 1 NOTE
-Skipped subordinate line Line 45: 2 TEST Empty Note
-Line ignored as not understood Line 46: 1 TEST Submitter
+Line ignored as not understood Line 43: 2 TEST Submitter Note
+Empty note ignored Line 44: 1 NOTE
+Skipped subordinate line Line 45: 2 TEST Empty Note
+Line ignored as not understood Line 46: 1 TEST Submitter
@@ -359,7 +359,7 @@ Line ignored as not understood Line 46:
Media Note
-
+ Media xref note
@@ -368,45 +368,45 @@ Line ignored as not understood Line 46:
Records not imported into INDI (individual) Gramps ID I0001:
-Empty note ignored Line 54: 2 NOTE
-Skipped subordinate line Line 55: 3 NOTE Empty note subordinate (Should be skipped)
-Skipped subordinate line Line 56: 3 TEST Empty Note
-Line ignored as not understood Line 58: 3 TEST 123456 Event Note
-Line ignored as not understood Line 62: 4 TEST Location Note
-Line ignored as not understood Line 63: 3 TEST Location
-Empty note ignored Line 66: 2 NOTE
-Skipped subordinate line Line 67: 3 TEST Empty Note
-Empty note ignored Line 68: 2 NOTE
-Skipped subordinate line Line 69: 3 TEST Empty Note
-Line ignored as not understood Line 73: 3 TEST FAMS Note
-Skipped subordinate line Line 74: 4 TEST skip on FAMS Note
-Line ignored as not understood Line 75: 2 TEST FAMS
-Line ignored as not understood Line 79: 2 TEST Person Note
-Line ignored as not understood Line 82: 2 TEST media
-Empty note ignored Line 85: 2 NOTE
-Skipped subordinate line Line 86: 3 TEST Empty Note
-Line ignored as not understood Line 88: 3 TEST 123456 Note
-Empty note ignored Line 90: 2 NOTE
-Skipped subordinate line Line 91: 3 TEST Empty Note
-Line ignored as not understood Line 94: 2 TEST REFN
+Empty note ignored Line 54: 2 NOTE
+Skipped subordinate line Line 55: 3 NOTE Empty note subordinate (Should be skipped)
+Skipped subordinate line Line 56: 3 TEST Empty Note
+Line ignored as not understood Line 58: 3 TEST 123456 Event Note
+Line ignored as not understood Line 62: 4 TEST Location Note
+Line ignored as not understood Line 63: 3 TEST Location
+Empty note ignored Line 66: 2 NOTE
+Skipped subordinate line Line 67: 3 TEST Empty Note
+Empty note ignored Line 68: 2 NOTE
+Skipped subordinate line Line 69: 3 TEST Empty Note
+Line ignored as not understood Line 73: 3 TEST FAMS Note
+Skipped subordinate line Line 74: 4 TEST skip on FAMS Note
+Line ignored as not understood Line 75: 2 TEST FAMS
+Line ignored as not understood Line 79: 2 TEST Person Note
+Line ignored as not understood Line 82: 2 TEST media
+Empty note ignored Line 85: 2 NOTE
+Skipped subordinate line Line 86: 3 TEST Empty Note
+Line ignored as not understood Line 88: 3 TEST 123456 Note
+Empty note ignored Line 90: 2 NOTE
+Skipped subordinate line Line 91: 3 TEST Empty Note
+Line ignored as not understood Line 94: 2 TEST REFN
Records not imported into NOTE Gramps ID N0018:
-Tag recognized but not supported Line 103: 1 RIN 123456
-Tag recognized but not supported Line 104: 1 REFN 98765
-Skipped subordinate line Line 105: 2 TYPE Who knows REFN TYPE
-Tag recognized but not supported Line 109: 2 NOTE Note on a change on a note!!!
-Skipped subordinate line Line 110: 3 CHAN
-Skipped subordinate line Line 111: 4 DATE 2001-01-11
-Skipped subordinate line Line 112: 5 TIME 16:00:06
+Tag recognized but not supported: '123456' on line Line 103: 1 RIN 123456
+Tag recognized but not supported: '98765' on line Line 104: 1 REFN 98765
+Skipped subordinate line Line 105: 2 TYPE Who knows REFN TYPE
+Tag recognized but not supported: 'Note on a change on a note!!!' on line Line 109: 2 NOTE Note on a change on a note!!!
+Skipped subordinate line Line 110: 3 CHAN
+Skipped subordinate line Line 111: 4 DATE 2001-01-11
+Skipped subordinate line Line 112: 5 TIME 16:00:06
@@ -415,27 +415,27 @@ Skipped subordinate line Line 112:
Records not imported into OBJE (multi-media object) Gramps ID M1:
-Could not import photo.jpg Line 114: 1 FILE photo.jpg
+Could not import photo.jpg Line 114: 1 FILE photo.jpg
-
+ Family Spouse reference NoteRecords not imported into INDI (individual) Gramps ID I0002:
-Empty note ignored Line 132: 2 NOTE
-Skipped subordinate line Line 133: 3 TEST Empty Note
+Empty note ignored Line 132: 2 NOTE
+Skipped subordinate line Line 133: 3 TEST Empty Note
-
+ Name note
@@ -446,31 +446,31 @@ Skipped subordinate line Line 133:
LDS xref note
-
+ Association link noteRecords not imported into INDI (individual) Gramps ID I0003:
-Empty note ignored Line 141: 2 NOTE
-Skipped subordinate line Line 142: 3 TEST Empty Note
-Line ignored as not understood Line 163: 3 TEST Accociation note
-Empty note ignored Line 167: 2 NOTE
-Skipped subordinate line Line 168: 3 TEST Empty Note
+Empty note ignored Line 141: 2 NOTE
+Skipped subordinate line Line 142: 3 TEST Empty Note
+Line ignored as not understood Line 163: 3 TEST Accociation note
+Empty note ignored Line 167: 2 NOTE
+Skipped subordinate line Line 168: 3 TEST Empty Note
-
+ Just for associationFamily xref note
-
+ Family note
@@ -481,43 +481,43 @@ Skipped subordinate line Line 168:
A sample text from a source of this family
-
+ A note this citation is on the FAMILY record.Records not imported into FAM (family) Gramps ID F0001:
-Line ignored as not understood Line 183: 2 TEST Family Note
-Empty note ignored Line 187: 1 NOTE
-Skipped subordinate line Line 188: 2 TEST Empty Note
-Line ignored as not understood Line 190: 2 TEST citation
-Line ignored as not understood Line 194: 3 TEST Citation Data
-Line ignored as not understood Line 196: 4 TEST Citation Data Note
+Line ignored as not understood Line 183: 2 TEST Family Note
+Empty note ignored Line 187: 1 NOTE
+Skipped subordinate line Line 188: 2 TEST Empty Note
+Line ignored as not understood Line 190: 2 TEST citation
+Line ignored as not understood Line 194: 3 TEST Citation Data
+Line ignored as not understood Line 196: 4 TEST Citation Data Note
-
+ A short note about the repository link.
-
+ note embedded in the SOURCE RecordRecords not imported into SOUR (source) Gramps ID S0001:
-Line ignored as not understood Line 206: 1 TEST source
-Empty note ignored Line 208: 2 NOTE
-Skipped subordinate line Line 209: 3 TEST Empty Note
-Empty note ignored Line 214: 1 NOTE
-Skipped subordinate line Line 215: 2 TEST Empty Note
+Line ignored as not understood Line 206: 1 TEST source
+Empty note ignored Line 208: 2 NOTE
+Skipped subordinate line Line 209: 3 TEST Empty Note
+Empty note ignored Line 214: 1 NOTE
+Skipped subordinate line Line 215: 2 TEST Empty Note
@@ -527,12 +527,12 @@ Skipped subordinate line Line 215:
Records not imported into REPO (repository) Gramps ID R0002:
-Line ignored as not understood Line 223: 1 TEST Repo
-Empty note ignored Line 224: 1 NOTE
-Skipped subordinate line Line 225: 2 TEST Empty Note
+Line ignored as not understood Line 223: 1 TEST Repo
+Empty note ignored Line 224: 1 NOTE
+Skipped subordinate line Line 225: 2 TEST Empty Note
diff --git a/data/tests/imp_notetest_lds_dfs.ged b/data/tests/imp_notetest_lds_dfs.ged
new file mode 100644
index 00000000000..7b5ff24d5f4
--- /dev/null
+++ b/data/tests/imp_notetest_lds_dfs.ged
@@ -0,0 +1,384 @@
+0 HEAD
+1 SOUR GEDitCOM
+2 NAME GEDitCOM
+2 VERS 2.9.4
+2 CORP RSAC Software
+1 SUBM @SUBMITTER@
+1 SUBN @SUBMISSION@
+1 DEST ANSTFILE
+1 DATE 1 JAN 1998
+2 TIME 13:57:24.80
+1 FILE imp_notetest.ged
+1 COPR Tom Tester 2016
+1 GEDC
+2 VERS 5.5
+2 FORM LINEAGE-LINKED
+1 LANG English
+1 NOTE Header note
+2 TEST Header Note
+1 NOTE
+2 TEST Empty Note
+1 CHAR UTF8
+0 @SUBMISSION@ SUBN
+1 SUBM @SUBMITTER@
+1 FAMF NameOfFamilyFile
+1 NOTE Submission Note
+2 TEST Submission Note
+1 NOTE
+2 TEST Empty Note
+1 TEST submission
+1 NOTE @N2@
+0 @N2@ NOTE Submission xref note
+1 RIN Submission Note RIN
+1 REFN Submission Note REFN
+2 TYPE Submission Note REFN TYPE
+1 SOUR Submission note source
+1 CHAN
+2 DATE 11 Jan 2001
+3 TIME 16:00:06
+1 TEST on XREF Note
+0 @SUBMITTER@ SUBM
+1 NAME John A. Tester
+1 NOTE Submitter Note
+2 TEST Submitter Note
+1 NOTE
+2 TEST Empty Note
+1 TEST Submitter
+1 NOTE @N3@
+0 @N3@ NOTE Submitter xref note
+0 @I1@ INDI
+1 NAME Tom /Tester/
+1 TEST person
+1 SEX M
+1 BIRT
+2 NOTE
+3 NOTE Empty note subordinate (Should be skipped)
+3 TEST Empty Note
+2 NOTE Birth Event note
+3 TEST 123456 Event Note
+2 DATE 15 JUN 1900
+2 ADDR 123 main, Norwalk, Ohio, USA
+3 NOTE Location Note
+4 TEST Location Note
+3 TEST Location
+3 NOTE Location Note 2
+1 DEAT
+2 NOTE
+3 TEST Empty Note
+2 NOTE
+3 TEST Empty Note
+2 NOTE @N5@
+1 FAMS @F1@
+2 NOTE FAMS Note
+3 TEST FAMS Note
+4 TEST skip on FAMS Note
+2 TEST FAMS
+2 NOTE FAMS Note 2
+1 NOTE @N4@
+1 NOTE Tom Tester Note
+2 TEST Person Note
+1 OBJE
+2 FORM URL
+2 TEST media
+2 TITL GEDCOM 5.5 documentation web site
+2 FILE http://homepages.rootsweb.com/~pmcbride/gedcom/55gctoc.htm
+2 NOTE
+3 TEST Empty Note
+2 NOTE Media Note
+3 TEST 123456 Note
+2 NOTE @N6@
+2 NOTE
+3 TEST Empty Note
+1 RIN 123456 Person
+1 REFN 98765 for PERSON
+2 TEST REFN
+2 TYPE Who knows OBJE REFN TYPE
+1 CHAN
+2 DATE 11 Jan 2001
+3 TIME 16:00:06
+1 OBJE @M1@
+1 _PPEXCLUDE
+1 _UID EAE358D5CAEC4F4A8146EFCAFB572480BF11
+1 _FSFTID LBYN-GFG
+1 _FSLINK https://familysearch.org
+0 @N4@ NOTE Tom Tester xref note
+0 @N5@ NOTE Death Event xref note
+0 @N6@ NOTE Media xref note
+1 RIN 123456
+1 REFN 98765
+2 TYPE Who knows REFN TYPE
+1 CHAN
+2 DATE 11 Jan 2001
+3 TIME 16:00:06
+2 NOTE Note on a change on a note!!!
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+0 @M1@ OBJE
+1 FILE photo.jpg
+2 FORM jpeg
+3 TYPE Film
+2 TITL Tom Tester's photo
+1 RIN 123456
+1 REFN 98765
+2 TYPE Who knows REFN TYPE
+1 CHAN
+2 DATE 11 Jan 2001
+3 TIME 16:00:06
+0 @I2@ INDI
+1 NAME Mrs /Tester/
+1 SEX F
+1 BIRT
+2 DATE 15 JUN 1901
+1 DEAT
+2 DATE 5 JUL 1975
+1 FAMS @F1@
+2 NOTE
+3 TEST Empty Note
+2 NOTE Family Spouse reference Note
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+0 @I3@ INDI
+1 NAME Ed /Tester/
+2 NICK Eddie
+2 NOTE
+3 TEST Empty Note
+2 NOTE Name note
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+1 SEX M
+1 BIRT
+2 DATE 15 JUN 1922
+1 DEAT
+2 DATE 5 JUL 1994
+1 FAMC @F1@
+1 BAPL
+2 DATE 5 MAY 0005 B.C.
+2 PLAC Salt Lake City
+3 NOTE Place note
+2 STAT Cleared
+2 TEMP SLAKE
+2 SOUR @SOURCE1@
+2 NOTE @N8@
+1 ASSO @I4@
+2 NOTE Association link note
+3 TEST Accociation note
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+2 NOTE
+3 TEST Empty Note
+0 @I4@ INDI
+1 NAME George /Testee/
+2 NOTE Just for association
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+0 @F1@ FAM
+1 HUSB @I1@
+2 _PREF Y
+1 TEST family
+1 WIFE @I2@
+2 _PREF Y
+1 CHIL @I3@
+2 _PREF Y
+1 SOUR @S1@
+1 NOTE @N7@
+1 NOTE Family note
+2 TEST Family Note
+2 CHAN
+3 DATE 11 Jan 2001
+4 TIME 16:00:06
+1 NOTE
+2 TEST Empty Note
+1 SOUR @S1@
+2 TEST citation
+2 PAGE 42
+2 DATA
+3 DATE 31 DEC 1900
+3 TEST Citation Data
+3 NOTE Citation Data Note
+4 TEST Citation Data Note
+3 TEXT A sample text from a source of this family
+2 QUAY 0
+2 NOTE A note this citation is on the FAMILY record.
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+0 @N7@ NOTE Family xref note
+0 @S1@ SOUR
+1 TITL Note Test file Source: Tester
+1 TEST source
+1 REPO @R1@
+2 NOTE
+3 TEST Empty Note
+2 NOTE A short note about the repository link.
+3 CHAN
+4 DATE 11 Jan 2001
+5 TIME 16:00:06
+1 NOTE
+2 TEST Empty Note
+1 NOTE note embedded in the SOURCE Record
+2 CHAN
+3 DATE 11 Jan 2001
+4 TIME 16:00:06
+0 @N8@ NOTE LDS xref note
+1 NOTE note embedded in the SOURCE Record
+2 CHAN
+3 DATE 11 Jan 2001
+4 TIME 16:00:06
+0 @S55@ SOUR
+1 ABBR 1860 United States Federal Census
+1 TITL 1860 United States Federal Census
+1 _PAREN Y
+0 @S33@ SOUR
+1 ABBR 1870 United States Federal Census
+1 TITL 1870 United States Federal Census
+1 _PAREN Y
+0 @S28@ SOUR
+1 ABBR 1880 United States Federal Census
+1 TITL 1880 United States Federal Census
+0 @S71@ SOUR
+1 ABBR FamilySearch Family Tree (https://www.familysearch.org)
+1 TITL "FamilySearch Family Tree," database, FamilySearch
+1 AUTH The Church of Jesus Christ of Latter-day Saints
+1 PUBL (https://www.familysearch.org)
+1 _ITALIC Y
+1 _PAREN Y
+0 @N8@ NOTE LDS xref note
+0 @R2@ REPO
+1 NAME The National Archives and Records Administration (NARA)
+1 ADDR The National Archives and Records Administration (NARA)
+2 _NAME The National Archives and Records Administration (NARA)
+2 ADR1 The National Archives and Records Administration (NARA)
+0 _PLAC_DEFN
+1 PLAC New Rochelle, New York
+2 ABBR New Rochelle, New York
+2 _RESOLVED -1
+0 _PLAC_DEFN
+1 PLAC Austin, Texas
+2 ABBR Austin, Texas
+2 _RESOLVED -1
+0 _PLAC_DEFN
+1 PLAC Cedar Rapids, Linn, Iowa, United States
+2 ABBR Cedar Rapids, Linn, Iowa, United States
+2 _RESOLVED -1
+0 _PLAC_DEFN
+1 PLAC Philadelphia, Philadelphia, Penn.
+2 ABBR Philadelphia, Philadelphia, Penn.
+2 _RESOLVED -1
+0 _PLAC_DEFN
+1 PLAC Oakland Cemetery, Philadelphia, Pennsylvania, United States
+2 ABBR Oakland Cemetery, Philadelphia, Pennsylvania, United States
+2 _RESOLVED -1
+0 _PLAC_DEFN
+1 PLAC Greenmount Cemetery, Philadelphia, Pennsylvania, United States
+2 ABBR Greenmount Cemetery, Philadelphia, Pennsylvania, United States
+2 _RESOLVED -1
+0 _EVENT_DEFN Adoption
+1 TYPE I
+1 TITL Adoption
+1 _SEN1 [HeShe] was adopted [onDate] [inPlace] by [Desc].
+1 _SEN2 [HeShe] was adopted [onDate].
+1 _SEN3 [HeShe] was adopted [inPlace].
+1 _SEN4 [HeShe] was adopted by [Desc].
+1 _SEN5 [HeShe] was adopted [onDate] by [Desc].
+1 _SEN6 [HeShe] was adopted [inPlace] by [Desc].
+1 _SEN7 [HeShe] was adopted [onDate] [inPlace].
+1 _SEN8 [HeShe] was adopted.
+1 ROLE Witness
+2 _SEN1 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] witnessed the adoption of [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] witnessed the adoption of [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] witnessed the adoption of [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] witnessed the adoption of [MainPerson].
+2 _DEF Y
+1 _DATE_TYPE 1
+1 _PLACE_TYPE 1
+1 _DESC_FLAG 1
+1 _INC_NOTES
+0 _EVENT_DEFN Alt. Birth
+1 TYPE I
+1 TITL Alt. Birth
+1 _SEN1 [HeShe] has conflicting birth information of [Desc], [Date] and [Place].
+1 _SEN2 [HeShe] has conflicting birth information of [Date].
+1 _SEN3 [HeShe] has conflicting birth information of [Place].
+1 _SEN4 [HeShe] has conflicting birth information of [Desc].
+1 _SEN5 [HeShe] has conflicting birth information of [Desc] and [Date].
+1 _SEN6 [HeShe] has conflicting birth information of [Desc] and [Place].
+1 _SEN7 [HeShe] has conflicting birth information of [Date] and [Place].
+1 _SEN8 [HeShe] has conflicting birth information.
+1 ROLE Doctor
+2 _SEN1 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] delivered [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] delivered [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] delivered [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] delivered [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] delivered [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] delivered [MainPerson].
+1 ROLE Father
+2 _SEN1 [FirstHeShe] is the father of [MainPerson] born [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] is the father of [MainPerson] born [onDate].
+2 _SEN3 [FirstHeShe] is the father of [MainPerson] born [inPlace].
+2 _SEN4 [FirstHeShe] is the father of [MainPerson] born: [Desc].
+2 _SEN5 [FirstHeShe] is the father of [MainPerson] born [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] is the father of [MainPerson] born [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] is the father of [MainPerson] born [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] is the father of [MainPerson] born.
+1 ROLE Godfather
+2 _SEN1 [FirstHeShe] is the godfather of [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] is the godfather of [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] is the godfather of [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] is the godfather of [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] is the godfather of [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] is the godfather of [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] is the godfather of [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] is the godfather of [MainPerson].
+1 ROLE Godmother
+2 _SEN1 [FirstHeShe] is the godmother of [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] is the godmother of [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] is the godmother of [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] is the godmother of [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] is the godmother of [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] is the godmother of [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] is the godmother of [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] is the godmother of [MainPerson].
+1 ROLE Midwife
+2 _SEN1 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] delivered [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] delivered [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] delivered [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] delivered [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] delivered [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] delivered [MainPerson].
+1 ROLE Mother
+2 _SEN1 [FirstHeShe] is the mother of [MainPerson] born [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] is the mother of [MainPerson] born [onDate].
+2 _SEN3 [FirstHeShe] is the mother of [MainPerson] born [inPlace].
+2 _SEN4 [FirstHeShe] is the mother of [MainPerson] born: [Desc].
+2 _SEN5 [FirstHeShe] is the mother of [MainPerson] born [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] is the mother of [MainPerson] born [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] is the mother of [MainPerson] born [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] is the mother of [MainPerson] born.
+1 ROLE Witness
+2 _SEN1 [FirstHeShe] witnessed the birth of [MainPerson] [onDate] [inPlace]: [Desc].
+2 _SEN2 [FirstHeShe] witnessed the birth of [MainPerson] [onDate].
+2 _SEN3 [FirstHeShe] witnessed the birth of [MainPerson] [inPlace].
+2 _SEN4 [FirstHeShe] witnessed the birth of [MainPerson]: [Desc].
+2 _SEN5 [FirstHeShe] witnessed the birth of [MainPerson] [onDate]: [Desc].
+2 _SEN6 [FirstHeShe] witnessed the birth of [MainPerson] [inPlace]: [Desc].
+2 _SEN7 [FirstHeShe] witnessed the birth of [MainPerson] [onDate] [inPlace].
+2 _SEN8 [FirstHeShe] witnessed the birth of [MainPerson].
+2 _DEF Y
+1 _DATE_TYPE 1
+1 _PLACE_TYPE 1
+1 _DESC_FLAG 1
+1 _INC_NOTES
+0 TRLR
\ No newline at end of file
diff --git a/data/tests/imp_notetest_lds_dfs.gramps b/data/tests/imp_notetest_lds_dfs.gramps
new file mode 100644
index 00000000000..60fa726c40d
--- /dev/null
+++ b/data/tests/imp_notetest_lds_dfs.gramps
@@ -0,0 +1,816 @@
+
+
+
+
+
+
+ John A. Tester
+
+
+
+
+
+
+
+ TEST
+ person
+
+
+ Birth
+
+
+
+
+
+ Death
+
+
+
+ _PPEXCLUDE
+
+
+ _FSLINK
+ https://familysearch.org
+
+
+ Birth
+
+
+
+ Death
+
+
+
+ Birth
+
+
+
+ Death
+
+
+
+ _PREF
+
+
+ TEST
+ family
+
+
+ _PREF
+
+
+
+
+ M
+
+ Tom
+ Tester
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ Mrs
+ Tester
+
+
+
+
+
+
+
+
+
+
+ M
+
+ Ed
+ Tester
+ Eddie
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ U
+
+ George
+ Testee
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+
+ 42
+ 0
+
+
+
+
+
+
+
+
+ Import from imp_notetest.ged
+ Tom Tester 2016
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @SOURCE1@
+
+
+ Note Test file Source: Tester
+
+
+
+
+
+
+
+ 1860 United States Federal Census
+ 1860 United States Federal Census
+
+
+
+ 1870 United States Federal Census
+ 1870 United States Federal Census
+
+
+
+ 1880 United States Federal Census
+ 1880 United States Federal Census
+
+
+ "FamilySearch Family Tree," database, <i>FamilySearch</i>
+ The Church of Jesus Christ of Latter-day Saints
+ (https://www.familysearch.org)
+ FamilySearch Family Tree (https://www.familysearch.org)
+
+
+
+
+
+ 123 main, Norwalk, Ohio, USA
+
+
+
+
+
+
+ Salt Lake City
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Business that produced the product: RSAC Software
+ GEDCOM data
+
+
+ SUBM (Submitter): (@SUBMITTER@) John A. Tester
+ GEDCOM data
+
+
+
+
+
+
+
+ Unknown
+ Unknown
+
+
+
+ The National Archives and Records Administration (NARA)
+ Library
+
+ The National Archives and Records Administration (NARA)
+
+
+
+
+
+
+ Header note
+
+
+
+ Records not imported into HEAD (header):
+
+Line ignored as not understood Line 18: 2 TEST Header Note
+Empty note ignored Line 19: 1 NOTE
+Skipped subordinate line Line 20: 2 TEST Empty Note
+
+
+
+
+ Submission Note
+
+
+
+ Submission xref note
+
+
+ Records not imported into Top Level:
+
+Line ignored as not understood Line 26: 2 TEST Submission Note
+Empty note ignored Line 27: 1 NOTE
+Skipped subordinate line Line 28: 2 TEST Empty Note
+Line ignored as not understood Line 29: 1 TEST submission
+
+
+
+
+ Records not imported into NOTE Gramps ID N0003:
+
+Tag recognized but not supported: 'Submission Note RIN' on line Line 32: 1 RIN Submission Note RIN
+Tag recognized but not supported: 'Submission Note REFN' on line Line 33: 1 REFN Submission Note REFN
+Skipped subordinate line Line 34: 2 TYPE Submission Note REFN TYPE
+Tag recognized but not supported: 'Submission note source' on line Line 35: 1 SOUR Submission note source
+Line ignored as not understood Line 39: 1 TEST on XREF Note
+
+
+
+
+ Submitter Note
+
+
+
+ Submitter xref note
+
+
+ Records not imported into SUBM (Submitter): (@SUBMITTER@) John A. Tester:
+
+Line ignored as not understood Line 43: 2 TEST Submitter Note
+Empty note ignored Line 44: 1 NOTE
+Skipped subordinate line Line 45: 2 TEST Empty Note
+Line ignored as not understood Line 46: 1 TEST Submitter
+
+
+
+
+ Birth Event note
+
+
+
+ Location Note
+
+
+
+ Location Note 2
+
+
+
+ Death Event xref note
+
+
+ FAMS Note
+
+
+
+ FAMS Note 2
+
+
+
+ Tom Tester xref note
+
+
+ Tom Tester Note
+
+
+
+ Media Note
+
+
+
+ Media xref note
+
+
+ Who knows OBJE REFN TYPE
+
+
+ Records not imported into INDI (individual) Gramps ID I0001:
+
+Empty note ignored Line 54: 2 NOTE
+Skipped subordinate line Line 55: 3 NOTE Empty note subordinate (Should be skipped)
+Skipped subordinate line Line 56: 3 TEST Empty Note
+Line ignored as not understood Line 58: 3 TEST 123456 Event Note
+Line ignored as not understood Line 62: 4 TEST Location Note
+Line ignored as not understood Line 63: 3 TEST Location
+Empty note ignored Line 66: 2 NOTE
+Skipped subordinate line Line 67: 3 TEST Empty Note
+Empty note ignored Line 68: 2 NOTE
+Skipped subordinate line Line 69: 3 TEST Empty Note
+Line ignored as not understood Line 73: 3 TEST FAMS Note
+Skipped subordinate line Line 74: 4 TEST skip on FAMS Note
+Line ignored as not understood Line 75: 2 TEST FAMS
+Line ignored as not understood Line 79: 2 TEST Person Note
+Line ignored as not understood Line 82: 2 TEST media
+Empty note ignored Line 85: 2 NOTE
+Skipped subordinate line Line 86: 3 TEST Empty Note
+Line ignored as not understood Line 88: 3 TEST 123456 Note
+Empty note ignored Line 90: 2 NOTE
+Skipped subordinate line Line 91: 3 TEST Empty Note
+Line ignored as not understood Line 94: 2 TEST REFN
+
+
+
+
+ Records not imported into NOTE Gramps ID N0018:
+
+Tag recognized but not supported: '123456' on line Line 107: 1 RIN 123456
+Tag recognized but not supported: '98765' on line Line 108: 1 REFN 98765
+Skipped subordinate line Line 109: 2 TYPE Who knows REFN TYPE
+Tag recognized but not supported: 'Note on a change on a note!!!' on line Line 113: 2 NOTE Note on a change on a note!!!
+Skipped subordinate line Line 114: 3 CHAN
+Skipped subordinate line Line 115: 4 DATE 2001-01-11
+Skipped subordinate line Line 116: 5 TIME 16:00:06
+
+
+
+
+ Who knows REFN TYPE
+
+
+ Records not imported into OBJE (multi-media object) Gramps ID M1:
+
+Could not import photo.jpg Line 118: 1 FILE photo.jpg
+
+
+
+
+ Family Spouse reference Note
+
+
+
+ Records not imported into INDI (individual) Gramps ID I0002:
+
+Empty note ignored Line 136: 2 NOTE
+Skipped subordinate line Line 137: 3 TEST Empty Note
+
+
+
+
+ Name note
+
+
+
+ Place note
+
+
+
+ LDS xref note
+
+
+ Association link note
+
+
+
+ Records not imported into INDI (individual) Gramps ID I0003:
+
+Empty note ignored Line 145: 2 NOTE
+Skipped subordinate line Line 146: 3 TEST Empty Note
+Line ignored as not understood Line 167: 3 TEST Accociation note
+Empty note ignored Line 171: 2 NOTE
+Skipped subordinate line Line 172: 3 TEST Empty Note
+
+
+
+
+ Just for association
+
+
+
+ Family xref note
+
+
+ Family note
+
+
+
+ Citation Data Note
+
+
+
+ A sample text from a source of this family
+
+
+ A note this citation is on the FAMILY record.
+
+
+
+ Records not imported into FAM (family) Gramps ID F0001:
+
+Tag recognized but not supported: 'Y' on line Line 186: 2 _PREF Y
+Line ignored as not understood Line 190: 2 TEST Family Note
+Empty note ignored Line 194: 1 NOTE
+Skipped subordinate line Line 195: 2 TEST Empty Note
+Line ignored as not understood Line 197: 2 TEST citation
+Line ignored as not understood Line 201: 3 TEST Citation Data
+Line ignored as not understood Line 203: 4 TEST Citation Data Note
+
+
+
+
+ A short note about the repository link.
+
+
+
+ note embedded in the SOURCE Record
+
+
+
+ Records not imported into SOUR (source) Gramps ID S0001:
+
+Line ignored as not understood Line 213: 1 TEST source
+Empty note ignored Line 215: 2 NOTE
+Skipped subordinate line Line 216: 3 TEST Empty Note
+Empty note ignored Line 221: 1 NOTE
+Skipped subordinate line Line 222: 2 TEST Empty Note
+
+
+
+
+ Records not imported into NOTE Gramps ID N0028:
+
+Line ignored as not understood Line 228: 1 NOTE note embedded in the SOURCE Record
+Skipped subordinate line Line 229: 2 CHAN
+Skipped subordinate line Line 230: 3 DATE 2001-01-11
+Skipped subordinate line Line 231: 4 TIME 16:00:06
+
+
+
+
+ Records not imported into SOUR (source) Gramps ID S0055:
+
+Tag recognized but not supported: 'Y' on line Line 235: 1 _PAREN Y
+
+
+
+
+ Records not imported into SOUR (source) Gramps ID S0033:
+
+Tag recognized but not supported: 'Y' on line Line 239: 1 _PAREN Y
+
+
+
+
+ Records not imported into SOUR (source) Gramps ID S0071:
+
+Tag recognized but not supported: 'Y' on line Line 248: 1 _ITALIC Y
+Tag recognized but not supported: 'Y' on line Line 249: 1 _PAREN Y
+
+
+
+
+ Records not imported into REPO (repository) Gramps ID R0003:
+
+Tag recognized but not supported: 'The National Archives and Records Administration (NARA)' on line Line 254: 2 _NAME The National Archives and Records Administration (NARA)
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 256: 0 _PLAC_DEFN
+Skipped subordinate line Line 257: 1 PLAC New Rochelle, New York
+Skipped subordinate line Line 258: 2 ABBR New Rochelle, New York
+Skipped subordinate line Line 259: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 260: 0 _PLAC_DEFN
+Skipped subordinate line Line 261: 1 PLAC Austin, Texas
+Skipped subordinate line Line 262: 2 ABBR Austin, Texas
+Skipped subordinate line Line 263: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 264: 0 _PLAC_DEFN
+Skipped subordinate line Line 265: 1 PLAC Cedar Rapids, Linn, Iowa, United States
+Skipped subordinate line Line 266: 2 ABBR Cedar Rapids, Linn, Iowa, United States
+Skipped subordinate line Line 267: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 268: 0 _PLAC_DEFN
+Skipped subordinate line Line 269: 1 PLAC Philadelphia, Philadelphia, Penn.
+Skipped subordinate line Line 270: 2 ABBR Philadelphia, Philadelphia, Penn.
+Skipped subordinate line Line 271: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 272: 0 _PLAC_DEFN
+Skipped subordinate line Line 273: 1 PLAC Oakland Cemetery, Philadelphia, Pennsylvania, United States
+Skipped subordinate line Line 274: 2 ABBR Oakland Cemetery, Philadelphia, Pennsylvania, United States
+Skipped subordinate line Line 275: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 276: 0 _PLAC_DEFN
+Skipped subordinate line Line 277: 1 PLAC Greenmount Cemetery, Philadelphia, Pennsylvania, United States
+Skipped subordinate line Line 278: 2 ABBR Greenmount Cemetery, Philadelphia, Pennsylvania, United States
+Skipped subordinate line Line 279: 2 _RESOLVED -1
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 280: 0 _EVENT_DEFN Adoption
+Skipped subordinate line Line 281: 1 TYPE I
+Skipped subordinate line Line 282: 1 TITL Adoption
+Skipped subordinate line Line 283: 1 _SEN1 [HeShe] was adopted [onDate] [inPlace] by [Desc].
+Skipped subordinate line Line 284: 1 _SEN2 [HeShe] was adopted [onDate].
+Skipped subordinate line Line 285: 1 _SEN3 [HeShe] was adopted [inPlace].
+Skipped subordinate line Line 286: 1 _SEN4 [HeShe] was adopted by [Desc].
+Skipped subordinate line Line 287: 1 _SEN5 [HeShe] was adopted [onDate] by [Desc].
+Skipped subordinate line Line 288: 1 _SEN6 [HeShe] was adopted [inPlace] by [Desc].
+Skipped subordinate line Line 289: 1 _SEN7 [HeShe] was adopted [onDate] [inPlace].
+Skipped subordinate line Line 290: 1 _SEN8 [HeShe] was adopted.
+Skipped subordinate line Line 291: 1 ROLE Witness
+Skipped subordinate line Line 292: 2 _SEN1 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 293: 2 _SEN2 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate].
+Skipped subordinate line Line 294: 2 _SEN3 [FirstHeShe] witnessed the adoption of [MainPerson] [inPlace].
+Skipped subordinate line Line 295: 2 _SEN4 [FirstHeShe] witnessed the adoption of [MainPerson]: [Desc].
+Skipped subordinate line Line 296: 2 _SEN5 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 297: 2 _SEN6 [FirstHeShe] witnessed the adoption of [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 298: 2 _SEN7 [FirstHeShe] witnessed the adoption of [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 299: 2 _SEN8 [FirstHeShe] witnessed the adoption of [MainPerson].
+Skipped subordinate line Line 300: 2 _DEF Y
+Skipped subordinate line Line 301: 1 _DATE_TYPE 1
+Skipped subordinate line Line 302: 1 _PLACE_TYPE 1
+Skipped subordinate line Line 303: 1 _DESC_FLAG 1
+Skipped subordinate line Line 304: 1 _INC_NOTES
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 305: 0 _EVENT_DEFN Alt. Birth
+Skipped subordinate line Line 306: 1 TYPE I
+Skipped subordinate line Line 307: 1 TITL Alt. Birth
+Skipped subordinate line Line 308: 1 _SEN1 [HeShe] has conflicting birth information of [Desc], [Date] and [Place].
+Skipped subordinate line Line 309: 1 _SEN2 [HeShe] has conflicting birth information of [Date].
+Skipped subordinate line Line 310: 1 _SEN3 [HeShe] has conflicting birth information of [Place].
+Skipped subordinate line Line 311: 1 _SEN4 [HeShe] has conflicting birth information of [Desc].
+Skipped subordinate line Line 312: 1 _SEN5 [HeShe] has conflicting birth information of [Desc] and [Date].
+Skipped subordinate line Line 313: 1 _SEN6 [HeShe] has conflicting birth information of [Desc] and [Place].
+Skipped subordinate line Line 314: 1 _SEN7 [HeShe] has conflicting birth information of [Date] and [Place].
+Skipped subordinate line Line 315: 1 _SEN8 [HeShe] has conflicting birth information.
+Skipped subordinate line Line 316: 1 ROLE Doctor
+Skipped subordinate line Line 317: 2 _SEN1 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 318: 2 _SEN2 [FirstHeShe] delivered [MainPerson] [onDate].
+Skipped subordinate line Line 319: 2 _SEN3 [FirstHeShe] delivered [MainPerson] [inPlace].
+Skipped subordinate line Line 320: 2 _SEN4 [FirstHeShe] delivered [MainPerson]: [Desc].
+Skipped subordinate line Line 321: 2 _SEN5 [FirstHeShe] delivered [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 322: 2 _SEN6 [FirstHeShe] delivered [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 323: 2 _SEN7 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 324: 2 _SEN8 [FirstHeShe] delivered [MainPerson].
+Skipped subordinate line Line 325: 1 ROLE Father
+Skipped subordinate line Line 326: 2 _SEN1 [FirstHeShe] is the father of [MainPerson] born [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 327: 2 _SEN2 [FirstHeShe] is the father of [MainPerson] born [onDate].
+Skipped subordinate line Line 328: 2 _SEN3 [FirstHeShe] is the father of [MainPerson] born [inPlace].
+Skipped subordinate line Line 329: 2 _SEN4 [FirstHeShe] is the father of [MainPerson] born: [Desc].
+Skipped subordinate line Line 330: 2 _SEN5 [FirstHeShe] is the father of [MainPerson] born [onDate]: [Desc].
+Skipped subordinate line Line 331: 2 _SEN6 [FirstHeShe] is the father of [MainPerson] born [inPlace]: [Desc].
+Skipped subordinate line Line 332: 2 _SEN7 [FirstHeShe] is the father of [MainPerson] born [onDate] [inPlace].
+Skipped subordinate line Line 333: 2 _SEN8 [FirstHeShe] is the father of [MainPerson] born.
+Skipped subordinate line Line 334: 1 ROLE Godfather
+Skipped subordinate line Line 335: 2 _SEN1 [FirstHeShe] is the godfather of [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 336: 2 _SEN2 [FirstHeShe] is the godfather of [MainPerson] [onDate].
+Skipped subordinate line Line 337: 2 _SEN3 [FirstHeShe] is the godfather of [MainPerson] [inPlace].
+Skipped subordinate line Line 338: 2 _SEN4 [FirstHeShe] is the godfather of [MainPerson]: [Desc].
+Skipped subordinate line Line 339: 2 _SEN5 [FirstHeShe] is the godfather of [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 340: 2 _SEN6 [FirstHeShe] is the godfather of [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 341: 2 _SEN7 [FirstHeShe] is the godfather of [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 342: 2 _SEN8 [FirstHeShe] is the godfather of [MainPerson].
+Skipped subordinate line Line 343: 1 ROLE Godmother
+Skipped subordinate line Line 344: 2 _SEN1 [FirstHeShe] is the godmother of [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 345: 2 _SEN2 [FirstHeShe] is the godmother of [MainPerson] [onDate].
+Skipped subordinate line Line 346: 2 _SEN3 [FirstHeShe] is the godmother of [MainPerson] [inPlace].
+Skipped subordinate line Line 347: 2 _SEN4 [FirstHeShe] is the godmother of [MainPerson]: [Desc].
+Skipped subordinate line Line 348: 2 _SEN5 [FirstHeShe] is the godmother of [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 349: 2 _SEN6 [FirstHeShe] is the godmother of [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 350: 2 _SEN7 [FirstHeShe] is the godmother of [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 351: 2 _SEN8 [FirstHeShe] is the godmother of [MainPerson].
+Skipped subordinate line Line 352: 1 ROLE Midwife
+Skipped subordinate line Line 353: 2 _SEN1 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 354: 2 _SEN2 [FirstHeShe] delivered [MainPerson] [onDate].
+Skipped subordinate line Line 355: 2 _SEN3 [FirstHeShe] delivered [MainPerson] [inPlace].
+Skipped subordinate line Line 356: 2 _SEN4 [FirstHeShe] delivered [MainPerson]: [Desc].
+Skipped subordinate line Line 357: 2 _SEN5 [FirstHeShe] delivered [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 358: 2 _SEN6 [FirstHeShe] delivered [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 359: 2 _SEN7 [FirstHeShe] delivered [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 360: 2 _SEN8 [FirstHeShe] delivered [MainPerson].
+Skipped subordinate line Line 361: 1 ROLE Mother
+Skipped subordinate line Line 362: 2 _SEN1 [FirstHeShe] is the mother of [MainPerson] born [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 363: 2 _SEN2 [FirstHeShe] is the mother of [MainPerson] born [onDate].
+Skipped subordinate line Line 364: 2 _SEN3 [FirstHeShe] is the mother of [MainPerson] born [inPlace].
+Skipped subordinate line Line 365: 2 _SEN4 [FirstHeShe] is the mother of [MainPerson] born: [Desc].
+Skipped subordinate line Line 366: 2 _SEN5 [FirstHeShe] is the mother of [MainPerson] born [onDate]: [Desc].
+Skipped subordinate line Line 367: 2 _SEN6 [FirstHeShe] is the mother of [MainPerson] born [inPlace]: [Desc].
+Skipped subordinate line Line 368: 2 _SEN7 [FirstHeShe] is the mother of [MainPerson] born [onDate] [inPlace].
+Skipped subordinate line Line 369: 2 _SEN8 [FirstHeShe] is the mother of [MainPerson] born.
+Skipped subordinate line Line 370: 1 ROLE Witness
+Skipped subordinate line Line 371: 2 _SEN1 [FirstHeShe] witnessed the birth of [MainPerson] [onDate] [inPlace]: [Desc].
+Skipped subordinate line Line 372: 2 _SEN2 [FirstHeShe] witnessed the birth of [MainPerson] [onDate].
+Skipped subordinate line Line 373: 2 _SEN3 [FirstHeShe] witnessed the birth of [MainPerson] [inPlace].
+Skipped subordinate line Line 374: 2 _SEN4 [FirstHeShe] witnessed the birth of [MainPerson]: [Desc].
+Skipped subordinate line Line 375: 2 _SEN5 [FirstHeShe] witnessed the birth of [MainPerson] [onDate]: [Desc].
+Skipped subordinate line Line 376: 2 _SEN6 [FirstHeShe] witnessed the birth of [MainPerson] [inPlace]: [Desc].
+Skipped subordinate line Line 377: 2 _SEN7 [FirstHeShe] witnessed the birth of [MainPerson] [onDate] [inPlace].
+Skipped subordinate line Line 378: 2 _SEN8 [FirstHeShe] witnessed the birth of [MainPerson].
+Skipped subordinate line Line 379: 2 _DEF Y
+Skipped subordinate line Line 380: 1 _DATE_TYPE 1
+Skipped subordinate line Line 381: 1 _PLACE_TYPE 1
+Skipped subordinate line Line 382: 1 _DESC_FLAG 1
+Skipped subordinate line Line 383: 1 _INC_NOTES
+
+
+
+
+ Objects referenced by this note were missing in a file imported on 12/25/1999 12:00:00 AM.
+
+
+
diff --git a/data/tests/imp_notetest_lds_in-out-in_dfs.ged b/data/tests/imp_notetest_lds_in-out-in_dfs.ged
new file mode 100644
index 00000000000..710d58e624a
--- /dev/null
+++ b/data/tests/imp_notetest_lds_in-out-in_dfs.ged
@@ -0,0 +1,198 @@
+0 HEAD
+1 SOUR Legacy
+2 VERS 10.0
+2 NAME Legacy (R)
+2 CORP MyHeritage USA Inc.
+3 ADDR 3098 W Executive Pkwy Ste 275
+4 CONT Lehi, UT 84043 USA
+1 DEST Legacy
+1 DATE 3 Aug 2026
+1 SUBM @S0@
+1 FILE C:\gramps_dev\gramps_legacy_import\data\tests\imp_notetest_dfs_lds.ged
+1 GEDC
+2 VERS 5.5.1
+2 FORM LINEAGE-LINKED
+1 CHAR UTF-8
+0 @S0@ SUBM
+1 NAME Not Given
+0 @I1@ INDI
+1 NAME Tom /Tester/
+2 GIVN Tom
+2 SURN Tester
+1 SEX M
+1 BIRT
+2 DATE 15 Jun 1900
+2 ADDR 123 main, Norwalk, Ohio, USA
+3 ADR1 123 main, Norwalk, Ohio, USA
+3 NOTE Location Note 2
+2 NOTE @BI1@
+1 DEAT
+2 NOTE @DI1@
+1 _PPEXCLUDE
+1 REFN 98765 for PERSON
+1 EVEN 98765 for PERSON
+2 TYPE Who knows OBJE REFN TYPE
+1 _UID 8D70ED25CB654853AC16E58EEB1FD4AB9847
+1 CHAN
+2 DATE 11 Jan 2001
+3 TIME 16:00
+1 OBJE
+2 FILE http://homepages.rootsweb.com/~pmcbride/gedcom/55gctoc.htm
+3 FORM URL
+4 TYPE URL
+3 TITL GEDCOM 5.5 documentation web site
+2 _PRIM Y
+1 OBJE
+2 FILE photo.jpg
+3 FORM jpg
+4 TYPE PHOTO
+3 TITL Tom Tester's photo
+2 REFN 98765
+1 FAMS @F1@
+1 NOTE @NI1@
+1 NOTE @HI1@
+0 @NI1@ NOTE
+1 CONC Tom Tester xref note
+1 CONT
+1 CONT Tom Tester Note
+0 @HI1@ NOTE
+1 CONC (Research):from yearTEST: TEST person
+1 CONT NOTE: NOTE Empty note subordinate (Should be skipped)
+1 CONT NOTE: TEST Empty Note
+1 CONT NOTE: TEST 123456 Event Note
+1 CONT NOTE: TEST Location Note
+1 CONT NOTE: TEST Person Note
+1 CONT OBJE: TEST media
+1 CONT NOTE: TEST 123456 Note
+1 CONT OBJE: RIN 123456
+0 @BI1@ NOTE
+1 CONC Birth Event note
+0 @DI1@ NOTE
+1 CONC Death Event xref note
+0 @I2@ INDI
+1 NAME Mrs /Tester/
+2 GIVN Mrs
+2 SURN Tester
+1 SEX F
+1 BIRT
+2 DATE 15 Jun 1901
+1 DEAT
+2 DATE 5 Jul 1975
+1 _UID C58628B95D0B41EEB3305DFE5F824E16463C
+1 CHAN
+2 DATE 3 Aug 2026
+3 TIME 18:39
+1 FAMS @F1@
+0 @I3@ INDI
+1 NAME Ed /Tester/
+2 GIVN Ed
+2 SURN Tester
+1 NAME Eddie /Tester/
+2 GIVN Eddie
+2 SURN Tester
+1 SEX M
+1 BIRT
+2 DATE 15 Jun 1922
+1 DEAT
+2 DATE 5 Jul 1994
+1 _PPEXCLUDE
+1 EVEN
+2 TYPE Association
+2 NOTE Association link note
+1 _UID DA9DFB151BEE490C983CBB0B6D9DCD4096C4
+1 BAPL
+2 DATE 5 May 0005 B.C.
+2 TEMP SLAKE - Salt Lake Temple
+1 CHAN
+2 DATE 3 Aug 2026
+3 TIME 18:38
+1 FAMC @F1@
+1 NOTE @NI3@
+1 NOTE @HI3@
+0 @NI3@ NOTE
+1 CONC Name note
+1 CONT
+1 CONT >LDS Baptism note: LDS xref note
+0 @HI3@ NOTE
+1 CONC (Research):from yearNOTE: TEST Empty Note
+1 CONT BAPL: SOUR @SOURCE1@
+1 CONT NOTE: TEST Accociation note
+0 @I4@ INDI
+1 NAME George /Testee/
+2 GIVN George
+2 SURN Testee
+1 SEX U
+1 _PPEXCLUDE
+1 _UID 77DA2714AB9D464DBAE1E44EB7A328974D11
+1 CHAN
+2 DATE 3 Aug 2026
+3 TIME 18:38
+1 NOTE @NI4@
+0 @NI4@ NOTE
+1 CONC Just for association
+0 @F1@ FAM
+1 HUSB @I1@
+2 _PREF Y
+1 WIFE @I2@
+2 _PREF Y
+1 MARR
+2 SOUR @S2@
+2 SOUR @S2@
+3 PAGE 42
+3 QUAY 0
+3 DATE 31 Dec 1900
+2 NOTE @NF1@
+1 CHIL @I3@
+2 _PREF Y
+1 CHAN
+2 DATE 3 Aug 2026
+3 TIME 18:38
+0 @NF1@ NOTE
+1 CONC Family xref note
+1 CONT Family note
+1 CONT
+1 CONT from year
+1 CONT TEST: TEST family
+0 _EVENT_DEFN Who knows OBJE REFN TYPE
+1 TYPE I
+1 TITL Who knows OBJE REFN TYPE
+1 _SEN1 [EventName]: [Desc], [onDate], [inPlace].
+1 _SEN2 [EventName]: [Date].
+1 _SEN3 [EventName]: [inPlace].
+1 _SEN4 [EventName]: [Desc].
+1 _SEN5 [EventName]: [Desc], [onDate].
+1 _SEN6 [EventName]: [Desc], [inPlace].
+1 _SEN7 [EventName]: [Date], [inPlace].
+1 _SEN8 [EventName].
+1 _DATE_TYPE 1
+1 _PLACE_TYPE 1
+1 _DESC_FLAG 1
+1 _INC_NOTES
+0 _EVENT_DEFN Association
+1 TYPE I
+1 TITL Association
+1 _SEN1 [EventName]: [Desc], [onDate], [inPlace].
+1 _SEN2 [EventName]: [Date].
+1 _SEN3 [EventName]: [inPlace].
+1 _SEN4 [EventName]: [Desc].
+1 _SEN5 [EventName]: [Desc], [onDate].
+1 _SEN6 [EventName]: [Desc], [inPlace].
+1 _SEN7 [EventName]: [Date], [inPlace].
+1 _SEN8 [EventName].
+1 _DATE_TYPE 1
+1 _PLACE_TYPE 1
+1 _DESC_FLAG 1
+1 _INC_NOTES
+0 @S2@ SOUR
+1 ABBR Note Test file Source: Tester
+1 TITL Note Test file Source: Tester
+1 NOTE note embedded in the SOURCE Record
+1 REPO @R1@
+1 _PAREN Y
+0 @R1@ REPO
+1 NAME The Testers repository
+1 ADDR The Testers repository
+2 _NAME The Testers repository
+2 ADR1 The Testers repository
+2 NOTE Repository Note
+0 TRLR
diff --git a/data/tests/imp_notetest_lds_in-out-in_dfs.gramps b/data/tests/imp_notetest_lds_in-out-in_dfs.gramps
new file mode 100644
index 00000000000..108737bde31
--- /dev/null
+++ b/data/tests/imp_notetest_lds_in-out-in_dfs.gramps
@@ -0,0 +1,397 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Birth
+
+
+
+
+
+ Death
+
+
+
+ _PPEXCLUDE
+
+
+ Who knows OBJE REFN TYPE
+ 98765 for PERSON
+
+
+ Birth
+
+
+
+ Death
+
+
+
+ Birth
+
+
+
+ Death
+
+
+
+ _PPEXCLUDE
+
+
+ Association
+
+
+
+ _PPEXCLUDE
+
+
+ _PREF
+
+
+ _PREF
+
+
+ Marriage
+
+
+
+
+
+
+
+ M
+
+ Tom
+ Tester
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ F
+
+ Mrs
+ Tester
+
+
+
+
+
+
+
+
+
+ M
+
+ Ed
+ Tester
+
+
+ Eddie
+ Tester
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ U
+
+ George
+ Testee
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+
+ 42
+ 0
+
+
+
+ 2
+
+
+
+
+
+ Import from imp_notetest_dfs_lds.ged
+
+
+
+
+
+
+
+
+
+
+
+
+ Note Test file Source: Tester
+ Note Test file Source: Tester
+
+
+
+
+
+
+
+ 123 main, Norwalk, Ohio, USA
+
+
+
+
+
+
+
+
+
+
+
+
+ Business that produced the product: MyHeritage USA Inc.
+ GEDCOM data
+
+ 3098 W Executive Pkwy Ste 275, Lehi, UT 84043 USA
+
+
+
+ SUBM (Submitter): (@S0@) Not Given
+ GEDCOM data
+
+
+
+
+ The Testers repository
+ Library
+
+ The Testers repository
+
+
+
+
+
+
+
+ Location Note 2
+
+
+
+ Birth Event note
+
+
+ Death Event xref note
+
+
+ Tom Tester xref note
+
+Tom Tester Note
+
+
+ (Research):from yearTEST: TEST person
+NOTE: NOTE Empty note subordinate (Should be skipped)
+NOTE: TEST Empty Note
+NOTE: TEST 123456 Event Note
+NOTE: TEST Location Note
+NOTE: TEST Person Note
+OBJE: TEST media
+NOTE: TEST 123456 Note
+OBJE: RIN 123456
+
+
+ Records not imported into INDI (individual) Gramps ID I0001:
+
+ADDR element ignored '123 main' Line 25: 2 ADDR 123 main, Norwalk, Ohio, USA
+ADDR element ignored ' Norwalk' Line 25: 2 ADDR 123 main, Norwalk, Ohio, USA
+ADDR element ignored ' Ohio' Line 25: 2 ADDR 123 main, Norwalk, Ohio, USA
+ADDR element ignored ' USA' Line 25: 2 ADDR 123 main, Norwalk, Ohio, USA
+Tag recognized but not supported: 'URL' on line Line 42: 4 TYPE URL
+Tag recognized but not supported: 'PHOTO' on line Line 48: 4 TYPE PHOTO
+Tag recognized but not supported: '98765' on line Line 50: 2 REFN 98765
+Could not import photo.jpg Line 45: 1 OBJE
+
+
+
+
+ Association link note
+
+
+
+ Name note
+
+>LDS Baptism note: LDS xref note
+
+
+ (Research):from yearNOTE: TEST Empty Note
+BAPL: SOUR @SOURCE1@
+NOTE: TEST Accociation note
+
+
+ Just for association
+
+
+ Family xref note
+Family note
+
+from year
+TEST: TEST family
+
+
+ Records not imported into FAM (family) Gramps ID F0001:
+
+Tag recognized but not supported: 'Y' on line Line 146: 2 _PREF Y
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 156: 0 _EVENT_DEFN Who knows OBJE REFN TYPE
+Skipped subordinate line Line 157: 1 TYPE I
+Skipped subordinate line Line 158: 1 TITL Who knows OBJE REFN TYPE
+Skipped subordinate line Line 159: 1 _SEN1 [EventName]: [Desc], [onDate], [inPlace].
+Skipped subordinate line Line 160: 1 _SEN2 [EventName]: [Date].
+Skipped subordinate line Line 161: 1 _SEN3 [EventName]: [inPlace].
+Skipped subordinate line Line 162: 1 _SEN4 [EventName]: [Desc].
+Skipped subordinate line Line 163: 1 _SEN5 [EventName]: [Desc], [onDate].
+Skipped subordinate line Line 164: 1 _SEN6 [EventName]: [Desc], [inPlace].
+Skipped subordinate line Line 165: 1 _SEN7 [EventName]: [Date], [inPlace].
+Skipped subordinate line Line 166: 1 _SEN8 [EventName].
+Skipped subordinate line Line 167: 1 _DATE_TYPE 1
+Skipped subordinate line Line 168: 1 _PLACE_TYPE 1
+Skipped subordinate line Line 169: 1 _DESC_FLAG 1
+Skipped subordinate line Line 170: 1 _INC_NOTES
+
+
+
+
+ Records not imported into Top Level:
+
+Unknown tag Line 171: 0 _EVENT_DEFN Association
+Skipped subordinate line Line 172: 1 TYPE I
+Skipped subordinate line Line 173: 1 TITL Association
+Skipped subordinate line Line 174: 1 _SEN1 [EventName]: [Desc], [onDate], [inPlace].
+Skipped subordinate line Line 175: 1 _SEN2 [EventName]: [Date].
+Skipped subordinate line Line 176: 1 _SEN3 [EventName]: [inPlace].
+Skipped subordinate line Line 177: 1 _SEN4 [EventName]: [Desc].
+Skipped subordinate line Line 178: 1 _SEN5 [EventName]: [Desc], [onDate].
+Skipped subordinate line Line 179: 1 _SEN6 [EventName]: [Desc], [inPlace].
+Skipped subordinate line Line 180: 1 _SEN7 [EventName]: [Date], [inPlace].
+Skipped subordinate line Line 181: 1 _SEN8 [EventName].
+Skipped subordinate line Line 182: 1 _DATE_TYPE 1
+Skipped subordinate line Line 183: 1 _PLACE_TYPE 1
+Skipped subordinate line Line 184: 1 _DESC_FLAG 1
+Skipped subordinate line Line 185: 1 _INC_NOTES
+
+
+
+
+ note embedded in the SOURCE Record
+
+
+
+ Records not imported into SOUR (source) Gramps ID S0002:
+
+Tag recognized but not supported: 'Y' on line Line 191: 1 _PAREN Y
+
+
+
+
+ Repository Note
+
+
+
+ Records not imported into REPO (repository) Gramps ID R0002:
+
+Tag recognized but not supported: 'The Testers repository' on line Line 195: 2 _NAME The Testers repository
+
+
+
+
+
diff --git a/data/tests/imp_sample.difs b/data/tests/imp_sample.difs
new file mode 100644
index 00000000000..2452bbb55cf
--- /dev/null
+++ b/data/tests/imp_sample.difs
@@ -0,0 +1,42 @@
+Mismatch on file: imp_sample.ged
+Note: N0015 handle=0000004800000048
+ Diff on: Note, text, string
+ 164: Records not imported into INDI (individual) Gramps ID I0018:
+
+Tag recognized but not supported: 'first generaton' on line Line 247: 2 TYPE first generaton
+
+ 164: Records not imported into INDI (individual) Gramps ID I0018:
+
+Tag recognized but not supported Line 247: 2 TYPE first generaton
+
+Note: N0022 handle=000000d0000000d0
+ Diff on: Note, text, string
+ 144: Records not imported into FAM (family) Gramps ID F0010:
+
+Tag recognized but not supported: '' on line Line 865: 2 _STAT
+
+ 144: Records not imported into FAM (family) Gramps ID F0010:
+
+Tag recognized but not supported Line 865: 2 _STAT
+
+Note: N0025 handle=000000e5000000e5
+ Diff on: Note, text, string
+ 182: Records not imported into FAM (family) Gramps ID F0008:
+
+Tag recognized but not supported: '123 Main st, Grantville, Virgi Line 1007: 1 ADDR 123 Main st, Grantville, Virginia, USA
+
+ 182: Records not imported into FAM (family) Gramps ID F0008:
+
+Tag recognized but not supported Line 1007: 1 ADDR 123 Main st, Grantville, Virginia, USA
+
+Note: N0031 handle=000000ef000000ef
+ Diff on: Note, text, string
+ 251: Records not imported into SOUR (source) Gramps ID S0003:
+
+Tag recognized but not supported: '' on line Line 1047: 1 DATA
+Skipped subordinate line Line 1048: 2 AGNC NYC Public Library
+
+ 251: Records not imported into SOUR (source) Gramps ID S0003:
+
+Tag recognized but not supported Line 1047: 1 DATA
+Skipped subordinate line Line 1048: 2 AGNC NYC Public Library
\ No newline at end of file
diff --git a/data/tests/imp_sample.gramps b/data/tests/imp_sample.gramps
index 73734e9c7c7..e8ffb431614 100644
--- a/data/tests/imp_sample.gramps
+++ b/data/tests/imp_sample.gramps
@@ -1,9 +1,9 @@
-
-
+
+
-
+ Alex Roitman,,,Not Provided
@@ -594,7 +594,7 @@
-
+ MGustaf
@@ -632,7 +632,7 @@
-
+ FKirsti Marie
@@ -643,7 +643,7 @@
-
+ FAstrid Shermanna
@@ -664,7 +664,7 @@
-
+ MHjalmar
@@ -685,7 +685,7 @@
-
+ MHjalmar
@@ -699,7 +699,7 @@
-
+ MGus
@@ -710,7 +710,7 @@
-
+ MCarl Emil
@@ -720,7 +720,7 @@
-
+ MHans Peter
@@ -733,7 +733,7 @@
-
+ MKeith Lloyd
@@ -744,7 +744,7 @@
-
+ FHanna
@@ -754,7 +754,7 @@
-
+ MHerman Julius
@@ -765,7 +765,7 @@
-
+ FEvelyn
@@ -775,7 +775,7 @@
-
+ FMarjorie Lee
@@ -787,7 +787,7 @@
-
+ FJennifer
@@ -799,7 +799,7 @@
-
+ FLillie Harriet
@@ -825,7 +825,7 @@
-
+ MEric Lloyd
@@ -836,7 +836,7 @@
-
+ FAmber Marie
@@ -861,7 +861,7 @@
-
+ FMarta
@@ -889,7 +889,7 @@
-
+ MIngeman
@@ -898,7 +898,7 @@
-
+ FAnna
@@ -908,7 +908,7 @@
-
+ MCraig Peter
@@ -918,7 +918,7 @@
-
+ XMagnes
@@ -930,7 +930,7 @@
-
+ FJanice Ann
@@ -941,7 +941,7 @@
-
+ FMarjorie
@@ -951,7 +951,7 @@
-
+ FDarcy
@@ -960,7 +960,7 @@
-
+ MLloyd
@@ -970,7 +970,7 @@
-
+ FAlice Paula
@@ -979,7 +979,7 @@
-
+ MLars Peter
@@ -996,7 +996,7 @@
-
+ FElna
@@ -1007,7 +1007,7 @@
-
+ MEdwin Michael
@@ -1023,7 +1023,7 @@
-
+ FKerstina
@@ -1033,7 +1033,7 @@
-
+ MMartin
@@ -1044,7 +1044,7 @@
-
+ MIngeman
@@ -1053,7 +1053,7 @@
-
+ FMarjorie Alice
@@ -1062,7 +1062,7 @@
-
+ FJanis Elaine
@@ -1071,7 +1071,7 @@
-
+ MMason Michael
@@ -1081,7 +1081,7 @@
-
+ MEdwin
@@ -1090,7 +1090,7 @@
-
+ FIngar
@@ -1099,7 +1099,7 @@
-
+ MEmil
@@ -1128,7 +1128,7 @@
-
+
@@ -1138,20 +1138,20 @@
-
+
-
+
-
+
@@ -1161,19 +1161,19 @@
-
+
-
+
-
+
@@ -1181,7 +1181,7 @@
-
+
@@ -1195,7 +1195,7 @@
-
+
@@ -1204,7 +1204,7 @@
-
+
@@ -1213,7 +1213,7 @@
-
+
@@ -1222,7 +1222,7 @@
-
+
@@ -1232,13 +1232,13 @@
-
+
-
+
@@ -1295,7 +1295,7 @@
@S999@
-
+ Birth Records
@@ -1306,14 +1306,14 @@
-
+ Birth Certificate
-
+ Birth, Death and Marriage Recordsgoodstuff
@@ -1468,7 +1468,7 @@
-
+ New York Public LibraryLibrary
@@ -1508,42 +1508,42 @@
Records not imported into HEAD (header):
-GEDCOM FORM not supported Line 14: 2 FORM NOT LINEAGE-LINKED
+GEDCOM FORM not supported Line 14: 2 FORM NOT LINEAGE-LINKED
Records not imported into SUBM (Submitter): (@SUBM@) Alex Roitman,,,:
-Line ignored as not understood Line 23: 2 NOTE No address provided (note not supported)
+Line ignored as not understood Line 23: 2 NOTE No address provided (note not supported)
Records not imported into Top Level:
-Line ignored as not understood Line 24: 0 SUBN SUBN
-Skipped subordinate line Line 25: 1 TEMP Mormon Temple
-Skipped subordinate line Line 26: 1 ANCE 4
-Skipped subordinate line Line 27: 1 DESC 4
-Skipped subordinate line Line 28: 1 ORDI Yes
+Line ignored as not understood Line 24: 0 SUBN SUBN
+Skipped subordinate line Line 25: 1 TEMP Mormon Temple
+Skipped subordinate line Line 26: 1 ANCE 4
+Skipped subordinate line Line 27: 1 DESC 4
+Skipped subordinate line Line 28: 1 ORDI Yes
Records not imported into FAM (family) Gramps ID F0003:
-Line ignored as not understood Line 46: 2 SOUR Not really allowed here
-Filename omitted Line 48: 1 OBJE
+Line ignored as not understood Line 46: 2 SOUR Not really allowed here
+Filename omitted Line 48: 1 OBJE
@@ -1579,11 +1579,11 @@ Filename omitted Line 48:
Records not imported into INDI (individual) Gramps ID I0016:
-Warn: ADDR overwritten Line 206: 3 ADR1 456 Main St again
-ADDR element ignored '459 Main St.' Line 204: 2 ADDR 459 Main St., The Village, San Francisco, CA, USA
+Warn: ADDR overwritten Line 206: 3 ADR1 456 Main St again
+ADDR element ignored '459 Main St.' Line 204: 2 ADDR 459 Main St., The Village, San Francisco, CA, USA
@@ -1592,10 +1592,10 @@ ADDR element ignored '459 Main St.' Line 204:
Records not imported into INDI (individual) Gramps ID I0018:
-Tag recognized but not supported Line 247: 2 TYPE first generaton
+Tag recognized but not supported: 'first generaton' on line Line 247: 2 TYPE first generaton
@@ -1624,38 +1624,38 @@ Company. He enlisted in the army at Sparks 7 December 1917 and served as a Corpo
Records not imported into FAM (family) Gramps ID F0010:
-Tag recognized but not supported Line 865: 2 _STAT
+Tag recognized but not supported: '' on line Line 865: 2 _STAT
Records not imported into FAM (family) Gramps ID F0011:
-Could not import Magnes&Anna_smiths_marr_cert.jpg Line 880: 3 OBJE
-Could not import Magnes&Anna_smiths_marr_cert.jpg Line 883: 2 OBJE
+Could not import Magnes&Anna_smiths_marr_cert.jpg Line 880: 3 OBJE
+Could not import Magnes&Anna_smiths_marr_cert.jpg Line 883: 2 OBJE
Records not imported into FAM (family) Gramps ID F0012:
-Could not import John&Alice_smiths_marr_cert.jpg Line 907: 1 OBJE
+Could not import John&Alice_smiths_marr_cert.jpg Line 907: 1 OBJE
Records not imported into FAM (family) Gramps ID F0008:
-Tag recognized but not supported Line 1007: 1 ADDR 123 Main st, Grantville, Virginia, USA
+Tag recognized but not supported: '123 Main st, Grantville, Virginia, USA' on line Line 1007: 1 ADDR 123 Main st, Grantville, Virginia, USA
@@ -1676,11 +1676,11 @@ Tag recognized but not supported Line 1007:
Records not imported into SOUR (source) Gramps ID S0003:
-Tag recognized but not supported Line 1047: 1 DATA
-Skipped subordinate line Line 1048: 2 AGNC NYC Public Library
+Tag recognized but not supported: '' on line Line 1047: 1 DATA
+Skipped subordinate line Line 1048: 2 AGNC NYC Public Library
@@ -1692,33 +1692,33 @@ Skipped subordinate line Line 1048:
Records not imported into REPO (repository) Gramps ID R0003:
-REFN ignored Line 1077: 3 REFN blah blah
-Skipped subordinate line Line 1078: 4 TYPE who knows
-Could not import Attic_photo.jpg Line 1081: 3 OBJE
+REFN ignored Line 1077: 3 REFN blah blah
+Skipped subordinate line Line 1078: 4 TYPE who knows
+Could not import Attic_photo.jpg Line 1081: 3 OBJE
Records not imported into Top Level:
-Unknown tag Line 1108: 0 XXX an unknown token at level 0
+Unknown tag Line 1108: 0 XXX an unknown token at level 0
Records not imported into Top Level:
-Unknown tag Line 1111: 1 @X1@ XXX and unknown token xref definition
+Unknown tag Line 1111: 1 @X1@ XXX and unknown token xref definition
-
+ Objects referenced by this note were missing in a file imported on 12/25/1999 12:00:00 AM.
diff --git a/gramps/plugins/lib/libgedcom.py b/gramps/plugins/lib/libgedcom.py
index 0b00f44d78a..44ee605abe3 100644
--- a/gramps/plugins/lib/libgedcom.py
+++ b/gramps/plugins/lib/libgedcom.py
@@ -1,8840 +1,8848 @@
-#
-# Gramps - a GTK+/GNOME based genealogy program
-#
-# Copyright (C) 2000-2007 Donald N. Allingham
-# Copyright (C) 2009-2010 Gary Burton
-# Copyright (C) 2010 Nick Hall
-# Copyright (C) 2011 Tim G L Lyons
-# Copyright (C) 2016 Paul R. Culley
-# Copyright (C) 2024 Doug Blank
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, see .
-#
-
-"""
-Import from GEDCOM
-
-The GEDCOM file format is defined by the GEDCOM 5.5 Specfication, found
-at http://www.familysearch.org/GEDCOM/GEDCOM55.EXE
-
-The basic structure is a line with three attributes:
-
-
-
-Because of this structure, it does not lend itself to more traditional
-parsing techniques, such as LALR. The LEVEL token implies too much to be
-useful in this context. While this makes parsing more difficult, it
-does provide one very beneficial feature for GEDCOM: Error recoverability.
-GEDCOM is a poorly implemented standard, primarily because it is a poor
-standard to begin with.
-
-Most commercial applications that implement GEDCOM output add custom
-extensions, and feel free to violate the existing structure. If one were
-cynical, one might believe that the commercial programs were trying to
-make it difficult to transfer your data to another application.
-
-This parser takes a different approach to parsing a GEDCOM file. The first
-state, Lexer, reads lines from the file, and does some basic lexical
-analysis on each line (actually several lines, since it automatically
-combines CONT and CONC tagged lines). Each logical line returned to this
-parser contains:
-
- Level, Token, Token text, Data, and line number.
-
-The Data field is typically text, but in some cases, it may be a integer
-value representing an enumerated type or a Gramps object (in the case of
-dates).
-
-The parser works on the current level. Each context and level has a an
-associated table (dictionary) of functions indexed by the corresponding
-TOKEN. When a token is found, we index into the table to find the function
-associated with the token. If no token is found, a function that skips the
-line and all subordinate (lines with a higher number). If a function is
-found, then we call that function, which in turn processes the line, and
-all tokens at the lower level.
-
-For example:
-
-1 BIRT
- 2 DATE 1 JAN 2000
- 2 UKNOWN TAG
- 3 NOTE DATA
-
-The function parsing the individual at level 1, would encounter the BIRT tag.
-It would look up the BIRT token in the table to see if a function as defined
-for this TOKEN, and pass control to this function. This function would then
-start parsing level 2. It would encounter the DATE tag, look up the
-corresponding function in the level 2 table, and pass control to its
-associated function. This function would terminate, and return control back to
-the level 2 parser, which would then encounter the "UKNOWN" tag. Since this is
-not a valid token, it would not be in the table, and a function that would skip
-all lines until the next level 2 token is found (in this case, skipping the
-"3 NOTE DATA" line.
-"""
-
-# -------------------------------------------------------------------------
-#
-# standard python modules
-#
-# -------------------------------------------------------------------------
-import os
-import re
-import time
-
-# from xml.parsers.expat import ParserCreate
-from collections import defaultdict, OrderedDict
-import string
-import mimetypes
-from io import StringIO, TextIOWrapper
-from urllib.parse import urlparse
-
-# ------------------------------------------------------------------------
-#
-# Set up logging
-#
-# ------------------------------------------------------------------------
-import logging
-
-LOG = logging.getLogger(".libgedcom")
-
-# -------------------------------------------------------------------------
-#
-# Gramps modules
-#
-# -------------------------------------------------------------------------
-from gramps.gen.const import GRAMPS_LOCALE as glocale
-
-_ = glocale.translation.gettext
-from gramps.gen.errors import GedcomError
-from gramps.gen.lib import (
- Address,
- Attribute,
- AttributeType,
- ChildRef,
- ChildRefType,
- Citation,
- Date,
- Event,
- EventRef,
- EventRoleType,
- EventType,
- Family,
- FamilyRelType,
- LdsOrd,
- Location,
- Media,
- MediaRef,
- Name,
- NameType,
- Note,
- NoteType,
- Person,
- PersonRef,
- Place,
- RepoRef,
- Repository,
- RepositoryType,
- Researcher,
- Source,
- SourceMediaType,
- SrcAttribute,
- Surname,
- Tag,
- Url,
- UrlType,
- PlaceType,
- PlaceRef,
- PlaceName,
-)
-from gramps.gen.lib.json_utils import data_to_object, object_to_dict
-from gramps.gen.db import DbTxn
-from gramps.gen.updatecallback import UpdateCallback
-from gramps.gen.utils.file import media_path
-from gramps.gen.utils.id import create_id
-from gramps.gen.utils.lds import TEMPLES
-from gramps.gen.utils.unknown import make_unknown, create_explanation_note
-from gramps.gen.datehandler._dateparser import DateParser
-from gramps.gen.db.dbconst import EVENT_KEY
-from gramps.gen.lib.const import IDENTICAL
-from gramps.gen.lib import StyledText, StyledTextTag, StyledTextTagType
-from gramps.gen.lib.urlbase import UrlBase
-from gramps.plugins.lib.libplaceimport import PlaceImport
-from gramps.gen.display.place import displayer as _pd
-from gramps.gen.utils.grampslocale import GrampsLocale
-
-# -------------------------------------------------------------------------
-#
-# constants
-#
-# -------------------------------------------------------------------------
-TOKEN_UNKNOWN = 0
-TOKEN_ABBR = 1
-TOKEN_ADDR = 2
-TOKEN_ADOP = 3
-TOKEN_ADR1 = 4
-TOKEN_ADR2 = 5
-TOKEN_AFN = 6
-TOKEN_IGNORE = 7
-TOKEN_REFN = 8
-TOKEN__AKA = 9
-TOKEN_ALIA = 11
-TOKEN_ANCI = 12
-TOKEN_ASSO = 13
-TOKEN_AUTH = 14
-TOKEN_BAPL = 15
-TOKEN_BIRT = 16
-TOKEN__CAT = 17
-TOKEN_CAUS = 18
-TOKEN_CHAN = 19
-TOKEN_CHAR = 20
-TOKEN_CHIL = 21
-TOKEN_CITY = 22
-TOKEN__COMM = 23
-TOKEN_CONC = 24
-TOKEN_CONT = 25
-TOKEN_COPR = 26
-TOKEN_CORP = 27
-TOKEN_CTRY = 28
-TOKEN_DATA = 29
-TOKEN_DATE = 30
-TOKEN_DEAT = 32
-TOKEN_DESI = 33
-TOKEN_DEST = 34
-TOKEN_ENDL = 35
-TOKEN_EVEN = 36
-TOKEN_FAM = 38
-TOKEN_FAMC = 39
-TOKEN_FAMS = 40
-TOKEN_FILE = 41
-TOKEN_FORM = 42
-TOKEN__FREL = 43
-TOKEN_GEDC = 44
-TOKEN_GIVN = 45
-TOKEN__GODP = 46
-TOKEN_HUSB = 47
-TOKEN_INDI = 48
-TOKEN_LABL = 49
-TOKEN_LANG = 50
-TOKEN__LOC = 51
-TOKEN__MARNM = 52
-TOKEN__MREL = 53
-TOKEN__NAME = 54
-TOKEN_NAME = 55
-TOKEN_NCHI = 56
-TOKEN_NICK = 57
-TOKEN_NOTE = 58
-TOKEN_NPFX = 59
-TOKEN_NSFX = 60
-TOKEN_OBJE = 61
-TOKEN_OFFI = 62
-TOKEN_PAGE = 63
-TOKEN_PEDI = 64
-TOKEN_PERI = 65
-TOKEN_PHON = 66
-TOKEN_PLAC = 67
-TOKEN_POST = 68
-TOKEN__PRIMARY = 69
-TOKEN__PRIV = 70
-TOKEN_PUBL = 71
-TOKEN_QUAY = 72
-TOKEN_RELI = 74
-TOKEN_REPO = 75
-TOKEN_RESI = 76
-TOKEN_RFN = 77
-TOKEN_RIN = 78
-TOKEN__SCHEMA = 79
-TOKEN_SEX = 80
-TOKEN_SLGC = 81
-TOKEN_SLGS = 82
-TOKEN_SOUR = 83
-TOKEN_SPFX = 84
-TOKEN_STAE = 85
-TOKEN__STAT = 86
-TOKEN_STAT = 87
-TOKEN_SUBM = 88
-TOKEN_SUBN = 89
-TOKEN_SURN = 90
-TOKEN_TAXT = 91
-TOKEN_TEMP = 92
-TOKEN_TEXT = 93
-TOKEN_TIME = 94
-TOKEN_TITL = 95
-TOKEN__TODO = 96
-TOKEN_TRLR = 97
-TOKEN_TYPE = 98
-TOKEN__UID = 99
-TOKEN_VERS = 100
-TOKEN_WIFE = 101
-TOKEN__WITN = 102
-TOKEN__WTN = 103
-TOKEN_AGNC = 104
-TOKEN_HEAD = 105
-TOKEN_CALN = 106
-TOKEN_MEDI = 107
-TOKEN_RELA = 108
-TOKEN__LKD = 109
-TOKEN_BLOB = 110
-TOKEN_CONL = 111
-TOKEN_AGE = 112
-TOKEN_RESN = 114
-TOKEN_ID = 115
-TOKEN_GEVENT = 116
-TOKEN_RNOTE = 117
-TOKEN_GATTR = 118
-TOKEN_ATTR = 119
-TOKEN_MAP = 120
-TOKEN_LATI = 121
-TOKEN_LONG = 122
-TOKEN_FACT = 123
-TOKEN_EMAIL = 124
-TOKEN_WWW = 125
-TOKEN_FAX = 126
-TOKEN_ROLE = 127
-TOKEN__MAR = 128
-TOKEN__MARN = 129
-TOKEN__ADPN = 130
-TOKEN__FSFTID = 131
-TOKEN__PHOTO = 132
-TOKEN__LINK = 133
-TOKEN__PRIM = 134
-TOKEN__JUST = 135
-TOKEN__TEXT = 136
-TOKEN__DATE = 137
-TOKEN__APID = 138
-TOKEN__CALLNAME = 139
-TOKEN_INIL = 140
-
-TOKENS = {
- "_ADPN": TOKEN__ADPN,
- "_AKA": TOKEN__AKA,
- "_AKAN": TOKEN__AKA,
- "_ALIA": TOKEN_ALIA,
- "_ANCES_ORDRE": TOKEN_IGNORE,
- "_APID": TOKEN__APID, # Ancestry.com database and page id
- "_CAT": TOKEN_IGNORE,
- "_CHUR": TOKEN_IGNORE,
- "_COMM": TOKEN__COMM,
- "_DATE": TOKEN__DATE,
- "_DATE2": TOKEN_IGNORE,
- "_DETAIL": TOKEN_IGNORE,
- "_EMAIL": TOKEN_EMAIL,
- "_E-MAIL": TOKEN_EMAIL,
- "_FREL": TOKEN__FREL,
- "_FSFTID": TOKEN__FSFTID,
- "_GODP": TOKEN__GODP,
- "_ITALIC": TOKEN_IGNORE,
- "_JUST": TOKEN__JUST, # FTM Citation Quality Justification
- "_LEVEL": TOKEN_IGNORE,
- "_LINK": TOKEN__LINK,
- "_LKD": TOKEN__LKD,
- "_LOC": TOKEN__LOC,
- "_MAR": TOKEN__MAR,
- "_MARN": TOKEN__MARN,
- "_MARNM": TOKEN__MARNM,
- "_MASTER": TOKEN_IGNORE,
- "_MEDI": TOKEN_MEDI,
- "_MREL": TOKEN__MREL,
- "_NAME": TOKEN__NAME,
- "_PAREN": TOKEN_IGNORE,
- "_PHOTO": TOKEN__PHOTO,
- "_PLACE": TOKEN_IGNORE,
- "_PREF": TOKEN__PRIMARY,
- "_PRIM": TOKEN__PRIM,
- "_PRIMARY": TOKEN__PRIMARY,
- "_PRIV": TOKEN__PRIV,
- "_PUBLISHER": TOKEN_IGNORE,
- "_RUFNAME": TOKEN__CALLNAME,
- "_SCBK": TOKEN_IGNORE,
- "_SCHEMA": TOKEN__SCHEMA,
- "_SSHOW": TOKEN_IGNORE,
- "_STAT": TOKEN__STAT,
- "_TEXT": TOKEN__TEXT,
- "_TODO": TOKEN__TODO,
- "_TYPE": TOKEN_TYPE,
- "_UID": TOKEN__UID,
- "_URL": TOKEN_WWW,
- "_WITN": TOKEN__WITN,
- "_WTN": TOKEN__WTN,
- "ABBR": TOKEN_ABBR,
- "ABBREVIATION": TOKEN_ABBR,
- "ADDR": TOKEN_ADDR,
- "ADDRESS": TOKEN_ADDR,
- "ADDRESS1": TOKEN_ADR1,
- "ADDRESS2": TOKEN_ADR2,
- "ADOP": TOKEN_ADOP,
- "ADOPT": TOKEN_ADOP,
- "ADR1": TOKEN_ADR1,
- "ADR2": TOKEN_ADR2,
- "AFN": TOKEN_AFN,
- "AGE": TOKEN_AGE,
- "AGENCY": TOKEN_IGNORE,
- "AGNC": TOKEN_AGNC,
- "AKA": TOKEN__AKA,
- "ALIA": TOKEN_ALIA,
- "ALIAS": TOKEN_ALIA,
- "ANCI": TOKEN_ANCI,
- "ASSO": TOKEN_ASSO,
- "ASSOCIATES": TOKEN_ASSO,
- "AUTH": TOKEN_AUTH,
- "AUTHOR": TOKEN_AUTH,
- "BAPL": TOKEN_BAPL,
- "BAPTISM-LDS": TOKEN_BAPL,
- "BIRT": TOKEN_BIRT,
- "BIRTH": TOKEN_BIRT,
- "BLOB": TOKEN_BLOB,
- "CALL_NUMBER": TOKEN_CALN,
- "CALN": TOKEN_CALN,
- "CAUS": TOKEN_CAUS,
- "CAUSE": TOKEN_CAUS,
- "CHAN": TOKEN_CHAN,
- "CHANGE": TOKEN_CHAN,
- "CHAR": TOKEN_CHAR,
- "CHARACTER": TOKEN_CHAR,
- "CHIL": TOKEN_CHIL,
- "CHILD": TOKEN_CHIL,
- "CHILDREN_COUNT": TOKEN_NCHI,
- "CITY": TOKEN_CITY,
- "CONC": TOKEN_CONC,
- "CONCATENATION": TOKEN_CONC,
- "CONCATENTATE": TOKEN_CONC,
- "CONL": TOKEN_CONL,
- "CONT": TOKEN_CONT,
- "CONTINUATION": TOKEN_CONT,
- "CONTINUED": TOKEN_CONT,
- "COPR": TOKEN_COPR,
- "COPYRIGHT": TOKEN_COPR,
- "CORP": TOKEN_CORP,
- "CORPORATION": TOKEN_CORP,
- "COUNTRY": TOKEN_CTRY,
- "CTRY": TOKEN_CTRY,
- "DATA": TOKEN_DATA,
- "DATE": TOKEN_DATE,
- "DEAT": TOKEN_DEAT,
- "DEATH": TOKEN_DEAT,
- "DESI": TOKEN_DESI,
- "DEST": TOKEN_DEST,
- "DESTINATION": TOKEN_DEST,
- "EMAI": TOKEN_EMAIL,
- "EMAIL": TOKEN_EMAIL,
- "ENDL": TOKEN_ENDL,
- "ENDOWMENT": TOKEN_ENDL,
- "EVEN": TOKEN_EVEN,
- "EVENT": TOKEN_EVEN,
- "FACT": TOKEN_FACT,
- "FAM": TOKEN_FAM,
- "FAMC": TOKEN_FAMC,
- "FAMILY": TOKEN_FAM,
- "FAMILY_CHILD": TOKEN_FAMC,
- "FAMILY_SPOUSE": TOKEN_FAMS,
- "FAMS": TOKEN_FAMS,
- "FAX": TOKEN_FAX,
- "FILE": TOKEN_FILE,
- "FORM": TOKEN_FORM,
- "GEDC": TOKEN_GEDC,
- "GEDCOM": TOKEN_GEDC,
- "GIVEN_NAME": TOKEN_GIVN,
- "GIVN": TOKEN_GIVN,
- "HEAD": TOKEN_HEAD,
- "HEADER": TOKEN_HEAD,
- "HUSB": TOKEN_HUSB,
- "HUSBAND": TOKEN_HUSB,
- "INDI": TOKEN_INDI,
- "INDIVIDUAL": TOKEN_INDI,
- "INIL": TOKEN_INIL,
- "LABEL": TOKEN_LABL,
- "LABL": TOKEN_LABL,
- "LANG": TOKEN_LANG,
- "LATI": TOKEN_LATI,
- "LONG": TOKEN_LONG,
- "MAP": TOKEN_MAP,
- "MEDI": TOKEN_MEDI,
- "MEDIA": TOKEN_MEDI,
- "NAME": TOKEN_NAME,
- "NAME_PREFIX": TOKEN_NPFX,
- "NAME_SUFFIX": TOKEN_NSFX,
- "NCHI": TOKEN_NCHI,
- "NICK": TOKEN_NICK,
- "NICKNAME": TOKEN_NICK,
- "NOTE": TOKEN_NOTE,
- "NPFX": TOKEN_NPFX,
- "NSFX": TOKEN_NSFX,
- "OBJE": TOKEN_OBJE,
- "OBJECT": TOKEN_OBJE,
- "OFFI": TOKEN_OFFI,
- "PAGE": TOKEN_PAGE,
- "PEDI": TOKEN_PEDI,
- "PEDIGREE": TOKEN_PEDI,
- "PERI": TOKEN_PERI,
- "PHON": TOKEN_PHON,
- "PHONE": TOKEN_PHON,
- "PHONE_NUMBER": TOKEN_PHON,
- "PLAC": TOKEN_PLAC,
- "PLACE": TOKEN_PLAC,
- "POST": TOKEN_POST,
- "POSTAL_CODE": TOKEN_POST,
- "PUBL": TOKEN_PUBL,
- "PUBLICATION": TOKEN_PUBL,
- "QUALITY_OF_DATA": TOKEN_QUAY,
- "QUAY": TOKEN_QUAY,
- "REFERENCE": TOKEN_REFN,
- "REFN": TOKEN_REFN,
- "RELA": TOKEN_RELA,
- "RELI": TOKEN_RELI,
- "RELIGION": TOKEN_RELI,
- "REPO": TOKEN_REPO,
- "REPOSITORY": TOKEN_REPO,
- "RESN": TOKEN_RESN,
- "RFN": TOKEN_RFN,
- "RIN": TOKEN_RIN,
- "ROLE": TOKEN_ROLE,
- "SCHEMA": TOKEN__SCHEMA,
- "SEX": TOKEN_SEX,
- "SLGC": TOKEN_SLGC,
- "SLGS": TOKEN_SLGS,
- "SOUR": TOKEN_SOUR,
- "SOURCE": TOKEN_SOUR,
- "SPFX": TOKEN_SPFX,
- "STAE": TOKEN_STAE,
- "STAT": TOKEN_STAT,
- "STATE": TOKEN_STAE,
- "STATUS": TOKEN_STAT,
- "SUBM": TOKEN_SUBM,
- "SUBMISSION": TOKEN_SUBN,
- "SUBMITTER": TOKEN_SUBM,
- "SUBN": TOKEN_SUBN,
- "SURN": TOKEN_SURN,
- "SURN_PREFIX": TOKEN_SPFX,
- "SURNAME": TOKEN_SURN,
- "TAXT": TOKEN_TAXT,
- "TEMP": TOKEN_TEMP,
- "TEMPLE": TOKEN_TEMP,
- "TEXT": TOKEN_TEXT,
- "TIME": TOKEN_TIME,
- "TITL": TOKEN_TITL,
- "TITLE": TOKEN_TITL,
- "TRAILER": TOKEN_TRLR,
- "TRLR": TOKEN_TRLR,
- "TYPE": TOKEN_TYPE,
- "URL": TOKEN_WWW,
- "VERS": TOKEN_VERS,
- "VERSION": TOKEN_VERS,
- "WIFE": TOKEN_WIFE,
- "WWW": TOKEN_WWW,
-}
-
-ADOPT_NONE = 0
-ADOPT_EVENT = 1
-ADOPT_FTW = 2
-ADOPT_LEGACY = 3
-ADOPT_PEDI = 4
-ADOPT_STD = 5
-CONC_OK = 0
-CONC_BROKEN = 1
-ALT_NAME_NONE = 0
-ALT_NAME_STD = 1
-ALT_NAME_ALIAS = 2
-ALT_NAME_AKA = 3
-ALT_NAME_EVENT_AKA = 4
-ALT_NAME_UALIAS = 5
-CALENDAR_NO = 0
-CALENDAR_YES = 1
-OBJE_NO = 0
-OBJE_YES = 1
-PREFIX_NO = 0
-PREFIX_YES = 1
-RESIDENCE_ADDR = 0
-RESIDENCE_PLAC = 1
-SOURCE_REFS_NO = 0
-SOURCE_REFS_YES = 1
-
-TYPE_BIRTH = ChildRefType()
-TYPE_ADOPT = ChildRefType(ChildRefType.ADOPTED)
-TYPE_FOSTER = ChildRefType(ChildRefType.FOSTER)
-
-RELATION_TYPES = (ChildRefType.BIRTH, ChildRefType.UNKNOWN, ChildRefType.NONE)
-
-PEDIGREE_TYPES = {
- "birth": ChildRefType(),
- "natural": ChildRefType(),
- "step": ChildRefType(ChildRefType.STEPCHILD),
- "adopted": TYPE_ADOPT,
- "foster": TYPE_FOSTER,
-}
-
-FTW_BAD_PLACE = [
- EventType.OCCUPATION,
- EventType.RELIGION,
- EventType.DEGREE,
-]
-
-MEDIA_MAP = {
- "audio": SourceMediaType.AUDIO,
- "book": SourceMediaType.BOOK,
- "card": SourceMediaType.CARD,
- "electronic": SourceMediaType.ELECTRONIC,
- "fiche": SourceMediaType.FICHE,
- "microfiche": SourceMediaType.FICHE,
- "microfilm": SourceMediaType.FICHE,
- "film": SourceMediaType.FILM,
- "magazine": SourceMediaType.MAGAZINE,
- "manuscript": SourceMediaType.MANUSCRIPT,
- "map": SourceMediaType.MAP,
- "newspaper": SourceMediaType.NEWSPAPER,
- "photo": SourceMediaType.PHOTO,
- "tombstone": SourceMediaType.TOMBSTONE,
- "grave": SourceMediaType.TOMBSTONE,
- "video": SourceMediaType.VIDEO,
-}
-
-OBJ_NOTETYPE = {
- "Attribute": NoteType.ATTRIBUTE,
- "Address": NoteType.ADDRESS,
- "Citation": NoteType.CITATION,
- "Event": NoteType.EVENT,
- "Family": NoteType.FAMILY,
- "LdsOrd": NoteType.LDS,
- "Media": NoteType.MEDIA,
- "Name": NoteType.GENERAL,
- "Place": NoteType.PLACE,
- "Person": NoteType.PERSON,
- "Repository": NoteType.REPO,
- "RepoRef": NoteType.REPOREF,
- "Source": NoteType.SOURCE,
- "PersonRef": NoteType.ASSOCIATION,
-}
-
-# -------------------------------------------------------------------------
-#
-# Integer to GEDCOM tag mappings for constants
-#
-# -------------------------------------------------------------------------
-CALENDAR_MAP_GEDCOM2XML = {
- "FRENCH R": Date.CAL_FRENCH,
- "JULIAN": Date.CAL_JULIAN,
- "HEBREW": Date.CAL_HEBREW,
-}
-
-QUALITY_MAP = {
- "CAL": Date.QUAL_CALCULATED,
- "INT": Date.QUAL_CALCULATED,
- "EST": Date.QUAL_ESTIMATED,
-}
-
-SEX_MAP = {
- "F": Person.FEMALE,
- "M": Person.MALE,
- "X": Person.OTHER,
-}
-
-FAMILYCONSTANTEVENTS = {
- EventType.ANNULMENT: "ANUL",
- EventType.DIV_FILING: "DIVF",
- EventType.DIVORCE: "DIV",
- EventType.CENSUS: "CENS",
- EventType.ENGAGEMENT: "ENGA",
- EventType.MARR_BANNS: "MARB",
- EventType.MARR_CONTR: "MARC",
- EventType.MARR_LIC: "MARL",
- EventType.MARR_SETTL: "MARS",
- EventType.MARRIAGE: "MARR",
-}
-
-PERSONALCONSTANTEVENTS = {
- EventType.ADOPT: "ADOP",
- EventType.ADULT_CHRISTEN: "CHRA",
- EventType.BIRTH: "BIRT",
- EventType.DEATH: "DEAT",
- EventType.BAPTISM: "BAPM",
- EventType.BAR_MITZVAH: "BARM",
- EventType.BAS_MITZVAH: "BASM",
- EventType.BLESS: "BLES",
- EventType.BURIAL: "BURI",
- # EventType.CAUSE_DEATH : "CAUS", Not legal Gedcom since v5.0
- EventType.ORDINATION: "ORDN",
- EventType.CENSUS: "CENS",
- EventType.CHRISTEN: "CHR",
- EventType.CONFIRMATION: "CONF",
- EventType.CREMATION: "CREM",
- EventType.DEGREE: "_DEG",
- EventType.DIV_FILING: "DIVF",
- EventType.EDUCATION: "EDUC",
- EventType.ELECTED: "_ELEC", # FTM custom tag
- EventType.EMIGRATION: "EMIG",
- EventType.FIRST_COMMUN: "FCOM",
- EventType.GRADUATION: "GRAD",
- EventType.MED_INFO: "_MDCL",
- EventType.MILITARY_SERV: "_MILT",
- EventType.NATURALIZATION: "NATU",
- EventType.NOB_TITLE: "TITL",
- EventType.NUM_MARRIAGES: "NMR",
- EventType.IMMIGRATION: "IMMI",
- EventType.OCCUPATION: "OCCU",
- EventType.PROBATE: "PROB",
- EventType.PROPERTY: "PROP",
- EventType.RELIGION: "RELI",
- EventType.RESIDENCE: "RESI",
- EventType.RETIREMENT: "RETI",
- EventType.WILL: "WILL",
-}
-
-FAMILYCONSTANTATTRIBUTES = {
- AttributeType.NUM_CHILD: "NCHI",
-}
-
-PERSONALCONSTANTATTRIBUTES = {
- AttributeType.CASTE: "CAST",
- AttributeType.DESCRIPTION: "DSCR",
- AttributeType.ID: "IDNO",
- AttributeType.NATIONAL: "NATI",
- AttributeType.NUM_CHILD: "NCHI",
- AttributeType.SSN: "SSN",
-}
-
-# -------------------------------------------------------------------------
-#
-# Gedcom to int constants
-#
-# -------------------------------------------------------------------------
-LDS_STATUS = {
- "BIC": LdsOrd.STATUS_BIC,
- "CANCELED": LdsOrd.STATUS_CANCELED,
- "CHILD": LdsOrd.STATUS_CHILD,
- "CLEARED": LdsOrd.STATUS_CLEARED,
- "COMPLETED": LdsOrd.STATUS_COMPLETED,
- "DNS": LdsOrd.STATUS_DNS,
- "INFANT": LdsOrd.STATUS_INFANT,
- "PRE-1970": LdsOrd.STATUS_PRE_1970,
- "QUALIFIED": LdsOrd.STATUS_QUALIFIED,
- "DNS/CAN": LdsOrd.STATUS_DNS_CAN,
- "STILLBORN": LdsOrd.STATUS_STILLBORN,
- "SUBMITTED": LdsOrd.STATUS_SUBMITTED,
- "UNCLEARED": LdsOrd.STATUS_UNCLEARED,
-}
-# -------------------------------------------------------------------------
-#
-# Custom event friendly names. These are non-standard GEDCOM "NEW_TAG"
-# tags that start with an '_' i.e. "_DNA". FTM has several of these, other
-# programs may have more. If a tag with this format is encountered it is
-# checked in this table for a "friendly" name translation and thereafter is
-# displayed and exported as such. If the tag is NOT in this table and not
-# otherwise handled by the code, the tag itself is used for display and
-# export. For example "_XYZ" is not in the table and will be displayed as
-# "_XYZ" and exported as an EVEN.TYPE=_XYZ
-# As Custom entries, they do not appear in Gramps Events add choice unless
-# already imported via GEDCOM.
-#
-# -------------------------------------------------------------------------
-CUSTOMEVENTTAGS = {
- "_CIRC": _("Circumcision"),
- "_COML": _("Common Law Marriage"),
- "_DEST": _("Destination"),
- "_DNA": _("DNA"),
- "_DCAUSE": _("Cause of Death"),
- "_EMPLOY": _("Employment"),
- "_EXCM": _("Excommunication"),
- "_EYC": _("Eye Color"),
- "_FUN": _("Funeral"),
- "_HEIG": _("Height"),
- "_INIT": _("Initiatory (LDS)"),
- "_MILTID": _("Military ID"),
- "_MISN": _("Mission (LDS)"),
- "_NAMS": _("Namesake"),
- "_ORDI": _("Ordinance"),
- "_ORIG": _("Origin"),
- "_SEPR": _("Separation"), # Applies to Families
- "_WEIG": _("Weight"),
-}
-# table for skipping illegal control chars in GEDCOM import
-# Only 09, 0A, 0D are allowed.
-STRIP_DICT = dict.fromkeys(list(range(9)) + list(range(11, 13)) + list(range(14, 32)))
-# The C1 Control characters are not treated in Latin-1 (ISO-8859-1) as
-# undefined, but if they have been used, the file is probably supposed to be
-# cp1252
-DEL_AND_C1 = dict.fromkeys(list(range(0x7F, 0x9F)))
-
-# -------------------------------------------------------------------------
-#
-# GEDCOM events to Gramps events conversion
-#
-# -------------------------------------------------------------------------
-GED_TO_GRAMPS_EVENT = {}
-for __val, __key in PERSONALCONSTANTEVENTS.items():
- if __key != "":
- GED_TO_GRAMPS_EVENT[__key] = __val
-GED_TO_GRAMPS_EVENT["Stillbirth"] = EventType.STILLBIRTH
-
-for __val, __key in FAMILYCONSTANTEVENTS.items():
- if __key != "":
- GED_TO_GRAMPS_EVENT[__key] = __val
-
-GED_TO_GRAMPS_ATTR = {}
-for __val, __key in PERSONALCONSTANTATTRIBUTES.items():
- if __key != "":
- GED_TO_GRAMPS_ATTR[__key] = __val
-
-# -------------------------------------------------------------------------
-#
-# GEDCOM Date Constants
-#
-# -------------------------------------------------------------------------
-HMONTH = [
- "",
- "TSH",
- "CSH",
- "KSL",
- "TVT",
- "SHV",
- "ADR",
- "ADS",
- "NSN",
- "IYR",
- "SVN",
- "TMZ",
- "AAV",
- "ELL",
-]
-
-FMONTH = [
- "",
- "VEND",
- "BRUM",
- "FRIM",
- "NIVO",
- "PLUV",
- "VENT",
- "GERM",
- "FLOR",
- "PRAI",
- "MESS",
- "THER",
- "FRUC",
- "COMP",
-]
-
-MONTH = [
- "",
- "JAN",
- "FEB",
- "MAR",
- "APR",
- "MAY",
- "JUN",
- "JUL",
- "AUG",
- "SEP",
- "OCT",
- "NOV",
- "DEC",
-]
-
-CALENDAR_MAP = {
- Date.CAL_HEBREW: (HMONTH, "@#DHEBREW@"),
- Date.CAL_FRENCH: (FMONTH, "@#DFRENCH R@"),
- Date.CAL_JULIAN: (MONTH, "@#DJULIAN@"),
- Date.CAL_SWEDISH: (MONTH, "@#DUNKNOWN@"),
-}
-
-CALENDAR_MAP_PARSESTRING = {
- Date.CAL_HEBREW: " (h)",
- Date.CAL_FRENCH: " (f)",
- Date.CAL_JULIAN: " (j)",
- Date.CAL_SWEDISH: " (s)",
-}
-
-# how wrong calendar use is shown
-CALENDAR_MAP_WRONGSTRING = {
- Date.CAL_HEBREW: " ",
- Date.CAL_FRENCH: " ",
- Date.CAL_JULIAN: " ",
- Date.CAL_SWEDISH: " ",
-}
-
-DATE_MODIFIER = {
- Date.MOD_ABOUT: "ABT",
- Date.MOD_BEFORE: "BEF",
- Date.MOD_AFTER: "AFT",
- Date.MOD_FROM: "FROM",
- Date.MOD_TO: "TO",
- # Date.MOD_INTERPRETED : "INT",
-}
-
-DATE_QUALITY = {
- Date.QUAL_CALCULATED: "CAL",
- Date.QUAL_ESTIMATED: "EST",
-}
-
-# -------------------------------------------------------------------------
-#
-# regular expressions
-#
-# -------------------------------------------------------------------------
-NOTE_RE = re.compile(r"\s*\d+\s+\@(\S+)\@\s+NOTE(.*)$")
-CONT_RE = re.compile(r"\s*\d+\s+CONT\s?(.*)$")
-CONC_RE = re.compile(r"\s*\d+\s+CONC\s?(.*)$")
-PERSON_RE = re.compile(r"\s*\d+\s+\@(\S+)\@\s+INDI(.*)$")
-MOD = re.compile(r"\s*(INT|EST|CAL)\s+(.*)$")
-CAL = re.compile(r"\s*(ABT|BEF|AFT|FROM|TO)?\s*@#D?([^@]+)@\s*(.*)$")
-RANGE = re.compile(r"\s*BET\s+@#D?([^@]+)@\s*(.*)\s+AND\s+@#D?([^@]+)@\s*(.*)$")
-RANGE1 = re.compile(r"\s*BET\s+\s*(.*)\s+AND\s+@#D?([^@]+)@\s*(.*)$")
-RANGE2 = re.compile(r"\s*BET\s+@#D?([^@]+)@\s*(.*)\s+AND\s+\s*(.*)$")
-SPAN = re.compile(r"\s*FROM\s+@#D?([^@]+)@\s*(.*)\s+TO\s+@#D?([^@]+)@\s*(.*)$")
-SPAN1 = re.compile(r"\s*FROM\s+\s*(.*)\s+TO\s+@#D?([^@]+)@\s*(.*)$")
-SPAN2 = re.compile(r"\s*FROM\s+@#D?([^@]+)@\s*(.*)\s+TO\s+\s*(.*)$")
-NAME_RE = re.compile(r"/?([^/]*)(/([^/]*)(/([^/]*))?)?")
-SURNAME_RE = re.compile(r"/([^/]*)/([^/]*)")
-# Bare numeric date in DATE values (e.g. "7/11/1959"). GEDCOM requires
-# 3-letter month names; numeric dates are out of spec and unavoidably
-# ambiguous between MM/DD/YYYY and DD/MM/YYYY. The parser interprets
-# them as MM/DD/YYYY (US format, see GedcomDateParser.dhformat_changed),
-# which silently swaps day and month for exports from tools that emit
-# DD/MM/YYYY (notably ancestry.com — see Mantis 9298). Used to flag
-# such dates with a warning so the user can audit them after import.
-BARE_NUMERIC_DATE = re.compile(r"^\s*\d{1,2}/\d{1,2}/\d{2,4}\s*$")
-
-
-# -----------------------------------------------------------------------
-#
-# GedcomDateParser
-#
-# -----------------------------------------------------------------------
-class GedcomDateParser(DateParser):
- """Parse the dates"""
-
- month_to_int = {
- "jan": 1,
- "feb": 2,
- "mar": 3,
- "apr": 4,
- "may": 5,
- "jun": 6,
- "jul": 7,
- "aug": 8,
- "sep": 9,
- "oct": 10,
- "nov": 11,
- "dec": 12,
- }
-
- _locale = GrampsLocale(lang="en_US") # no register_datehandler here
-
- def dhformat_changed(self):
- """Allow overriding so a subclass can modify it"""
- self.dhformat = "%m/%d/%y"
-
-
-# -------------------------------------------------------------------------
-#
-# Lexer - serves as the lexical analysis engine
-#
-# -------------------------------------------------------------------------
-class Lexer:
- """low level line reading and early parsing"""
-
- def __init__(self, ifile, __add_msg):
- self.ifile = ifile
- self.current_list = []
- self.eof = False
- self.cnv = None
- self.cnt = 0
- self.index = 0
- self.func_map = {
- TOKEN_CONT: self.__fix_token_cont,
- TOKEN_CONC: self.__fix_token_conc,
- }
- self.__add_msg = __add_msg
-
- def readline(self):
- """read a line from file with possibility of putting it back"""
- if len(self.current_list) <= 1 and not self.eof:
- self.__readahead()
- try:
- return GedLine(self.current_list.pop())
- except:
- LOG.debug("Error in reading Gedcom line", exc_info=True)
- return None
-
- def __fix_token_cont(self, data):
- line = self.current_list[0]
- new_value = line[2] + "\n" + data[2]
- self.current_list[0] = (line[0], line[1], new_value, line[3], line[4])
-
- def __fix_token_conc(self, data):
- line = self.current_list[0]
- if len(line[2]) == 4:
- # This deals with lines of the form
- # 0 @@ NOTE
- # 1 CONC
- # The previous line contains only a tag and no data so concat a
- # space to separate the new line from the tag. This prevents the
- # first letter of the new line being lost later
- # in _GedcomParse.__parse_record
- new_value = line[2] + " " + data[2]
- else:
- new_value = line[2] + data[2]
- self.current_list[0] = (line[0], line[1], new_value, line[3], line[4])
-
- def __readahead(self):
- while len(self.current_list) < 5:
- line = self.ifile.readline()
- self.index += 1
- if not line:
- self.eof = True
- return
-
- original_line = line
- try:
- # According to the GEDCOM 5.5 standard,
- # Chapter 1 subsection Grammar "leading whitespace preceeding
- # a GEDCOM line should be ignored"
- # We will also strip the terminator which is any combination
- # of carriage_return and line_feed
- line = line.lstrip(" ").rstrip("\n\r")
- # split into level+delim+rest
- line = line.partition(" ")
- level = int(line[0])
- # there should only be one space after the level,
- # but we can ignore more,
- line = line[2].lstrip(" ")
- # then split into tag+delim+line_value
- # or xfef_id+delim+rest
- # the xref_id can have spaces in it
- if line.startswith("@"):
- line = line.split("@", 2)
- # line is now [None, alphanum+pointer_string, rest]
- tag = "@" + line[1] + "@"
- line_value = line[2].lstrip()
- # Ignore meaningless @IDENT@ on CONT or CONC line
- # as noted at https://www.tamurajones.net/IdentCONT.xhtml
- if line_value.startswith(("CONT ", "CONC ")):
- line = line_value.partition(" ")
- tag = line[0]
- line_value = line[2]
- else:
- line = line.partition(" ")
- tag = line[0]
- line_value = line[2]
- except:
- problem = _("Line ignored ")
- text = original_line.rstrip("\n\r")
- prob_width = 66
- problem = problem.ljust(prob_width)[0 : (prob_width - 1)]
- text = text.replace("\n", "\n".ljust(prob_width + 22))
- message = "%s %s" % (problem, text)
- self.__add_msg(message)
- continue
-
- # Need to un-double '@' See Gedcom 5.5 spec 'any_char'
- line_value = line_value.replace("@@", "@")
- token = TOKENS.get(tag, TOKEN_UNKNOWN)
- data = (level, token, line_value, tag, self.index)
-
- func = self.func_map.get(data[1])
- if func:
- func(data)
- else:
- # There will normally only be one space between tag and
- # line_value, but in case there is more then one, remove extra
- # spaces after CONC/CONT processing
- # Also, Gedcom spec says there should be no spaces at end of
- # line, however some programs put them there (FTM), so let's
- # leave them in place.
- data = data[:2] + (data[2].lstrip(),) + data[3:]
- self.current_list.insert(0, data)
-
- def clean_up(self):
- """
- Break circular references to parsing methods stored in dictionaries
- to aid garbage collection
- """
- for key in list(self.func_map.keys()):
- del self.func_map[key]
- del self.func_map
-
-
-# -----------------------------------------------------------------------
-#
-# GedLine - represents a tokenized version of a GEDCOM line
-#
-# -----------------------------------------------------------------------
-class GedLine:
- """
- GedLine is a class the represents a GEDCOM line. The form of a GEDCOM line
- is:
-
-
-
- This gets parsed into
-
- Line Number, Level, Token Value, Token Text, and Data
-
- Data is dependent on the context the Token Value. For most of tokens,
- this is just a text string. However, for certain tokens where we know
- the context, we can provide some value. The current parsed tokens are:
-
- TOKEN_DATE - Date
- TOKEN_SEX - Person gender item
- TOEKN_UKNOWN - Check to see if this is a known event
- """
-
- __DATE_CNV = GedcomDateParser()
-
- @staticmethod
- def __extract_date(text):
- """
- Converts the specified text to a Date object.
- """
- dateobj = Date()
- # Horrible hack for importing illegal GEDCOM from Apple Macintosh
- # Classic 'Gene' program
- text = text.replace("BET ABT", "EST BET")
-
- # extract out the MOD line
- match = MOD.match(text)
- mod = ""
- if match:
- mod, text = match.groups()
- qual = QUALITY_MAP.get(mod, Date.QUAL_NONE)
- mod += " "
- else:
- qual = Date.QUAL_NONE
-
- # parse the range if we match, if so, return
- match = RANGE.match(text)
- match1 = RANGE1.match(text)
- match2 = RANGE2.match(text)
- if match or match1 or match2:
- if match:
- cal1, data1, cal2, data2 = match.groups()
- elif match1:
- cal1 = Date.CAL_GREGORIAN
- data1, cal2, data2 = match1.groups()
- elif match2:
- cal2 = Date.CAL_GREGORIAN
- cal1, data1, data2 = match2.groups()
- cal1 = CALENDAR_MAP_GEDCOM2XML.get(cal1, Date.CAL_GREGORIAN)
- cal2 = CALENDAR_MAP_GEDCOM2XML.get(cal2, Date.CAL_GREGORIAN)
- if cal1 != cal2:
- # not supported by GRAMPS, import as text, we construct a string
- # that the parser will not parse as a correct date
- return GedLine.__DATE_CNV.parse(
- "%sbetween %s%s and %s%s"
- % (
- mod,
- data1,
- CALENDAR_MAP_WRONGSTRING.get(cal1, ""),
- CALENDAR_MAP_WRONGSTRING.get(cal2, ""),
- data2,
- )
- )
-
- # add hebrew, ... calendar so that months are recognized
- data1 += CALENDAR_MAP_PARSESTRING.get(cal1, "")
- data2 += CALENDAR_MAP_PARSESTRING.get(cal2, "")
- start = GedLine.__DATE_CNV.parse(data1)
- stop = GedLine.__DATE_CNV.parse(data2)
- dateobj.set(
- Date.QUAL_NONE,
- Date.MOD_RANGE,
- cal1,
- start.get_start_date() + stop.get_start_date(),
- )
- dateobj.set_quality(qual)
- return dateobj
-
- # parse a span if we match
- match = SPAN.match(text)
- match1 = SPAN1.match(text)
- match2 = SPAN2.match(text)
- if match or match1 or match2:
- if match:
- cal1, data1, cal2, data2 = match.groups()
- elif match1:
- cal1 = Date.CAL_GREGORIAN
- data1, cal2, data2 = match1.groups()
- elif match2:
- cal2 = Date.CAL_GREGORIAN
- cal1, data1, data2 = match2.groups()
- cal1 = CALENDAR_MAP_GEDCOM2XML.get(cal1, Date.CAL_GREGORIAN)
- cal2 = CALENDAR_MAP_GEDCOM2XML.get(cal2, Date.CAL_GREGORIAN)
- if cal1 != cal2:
- # not supported by GRAMPS, import as text, we construct a string
- # that the parser will not parse as a correct date
- return GedLine.__DATE_CNV.parse(
- "%sfrom %s%s to %s%s"
- % (
- mod,
- data1,
- CALENDAR_MAP_WRONGSTRING.get(cal1, ""),
- CALENDAR_MAP_WRONGSTRING.get(cal2, ""),
- data2,
- )
- )
- # add hebrew, ... calendar so that months are recognized
- data1 += CALENDAR_MAP_PARSESTRING.get(cal1, "")
- data2 += CALENDAR_MAP_PARSESTRING.get(cal2, "")
- start = GedLine.__DATE_CNV.parse(data1)
- stop = GedLine.__DATE_CNV.parse(data2)
- dateobj.set(
- Date.QUAL_NONE,
- Date.MOD_SPAN,
- cal1,
- start.get_start_date() + stop.get_start_date(),
- )
- dateobj.set_quality(qual)
- return dateobj
-
- match = CAL.match(text)
- if match:
- abt, call, data = match.groups()
- call = CALENDAR_MAP_GEDCOM2XML.get(call, Date.CAL_GREGORIAN)
- data += CALENDAR_MAP_PARSESTRING.get(call, "")
- if abt:
- dateobj = GedLine.__DATE_CNV.parse("%s %s" % (abt, data))
- else:
- dateobj = GedLine.__DATE_CNV.parse(data)
- dateobj.set_quality(qual)
- return dateobj
- dateobj = GedLine.__DATE_CNV.parse(text)
- dateobj.set_quality(qual)
- return dateobj
-
- def __init__(self, data):
- """
- If the level is 0, then this is a top level instance. In this case,
- we may find items in the form of:
-
- @ID@
-
- If this is not the top level, we check the MAP_DATA array to see if
- there is a conversion function for the data.
- """
- self.line = data[4]
- self.level = data[0]
- self.token = data[1]
- self.token_text = data[3].strip()
- self.data = str(data[2])
-
- if self.level == 0:
- if (
- self.token_text
- and self.token_text[0] == "@"
- and self.token_text[-1] == "@"
- ):
- self.token = TOKEN_ID
- self.token_text = self.token_text[1:-1]
- self.data = self.data.strip()
- else:
- func = _MAP_DATA.get(self.token)
- if func:
- func(self)
-
- def calc_sex(self):
- """
- Converts the data field to a gen.lib token indicating the gender
- """
- try:
- self.data = SEX_MAP.get(self.data.strip()[0], Person.UNKNOWN)
- except:
- self.data = Person.UNKNOWN
-
- def calc_date(self):
- """
- Converts the data field to a Date object
- """
- self.data = self.__extract_date(self.data)
- self.token = TOKEN_DATE
-
- def calc_unknown(self):
- """
- Checks to see if the token maps a known GEDCOM event. If so, we
- change the type from UNKNOWN to TOKEN_GEVENT (gedcom event), and
- the data is assigned to the associated Gramps EventType
- """
- token = GED_TO_GRAMPS_EVENT.get(self.token_text)
- if token:
- event = Event()
- event.set_description(self.data)
- event.set_type(token)
- self.token = TOKEN_GEVENT
- self.data = event
- else:
- token = GED_TO_GRAMPS_ATTR.get(self.token_text)
- if token:
- attr = Attribute()
- attr.set_value(self.data)
- attr.set_type(token)
- self.token = TOKEN_ATTR
- self.data = attr
-
- def calc_note(self):
- """look for a note xref @N0001@"""
- gid = self.data.strip()
- if len(gid) > 2 and gid[0] == "@" and gid[-1] == "@":
- self.token = TOKEN_RNOTE
- self.data = gid[1:-1]
-
- def calc_nchi(self):
- """set attribute for number of children"""
- attr = Attribute()
- attr.set_value(self.data)
- attr.set_type(AttributeType.NUM_CHILD)
- self.data = attr
- self.token = TOKEN_ATTR
-
- def calc_attr(self):
- """set attribure for general attributes"""
- attr = Attribute()
- attr.set_value(self.data)
- attr.set_type((AttributeType.CUSTOM, self.token_text))
- self.data = attr
- self.token = TOKEN_ATTR
-
- def __repr__(self):
- return "%d: %d (%d:%s) %s" % (
- self.line,
- self.level,
- self.token,
- self.token_text,
- self.data,
- )
-
-
-_MAP_DATA = {
- TOKEN_UNKNOWN: GedLine.calc_unknown,
- TOKEN_DATE: GedLine.calc_date,
- TOKEN__DATE: GedLine.calc_date,
- TOKEN_SEX: GedLine.calc_sex,
- TOKEN_NOTE: GedLine.calc_note,
- TOKEN_NCHI: GedLine.calc_nchi,
- TOKEN__STAT: GedLine.calc_attr,
- TOKEN__UID: GedLine.calc_attr,
- TOKEN_AFN: GedLine.calc_attr,
- TOKEN__FSFTID: GedLine.calc_attr,
-}
-
-
-# -------------------------------------------------------------------------
-#
-# File Readers
-#
-# -------------------------------------------------------------------------
-class BaseReader:
- """base char level reader"""
-
- def __init__(self, ifile, encoding, __add_msg):
- self.ifile = ifile
- self.enc = encoding
- self.__add_msg = __add_msg
-
- def reset(self):
- """return to beginning"""
- self.ifile.seek(0)
-
- def readline(self):
- """Read a single line"""
- raise NotImplementedError()
-
- def report_error(self, problem, line):
- """Create an error message"""
- line = line.rstrip("\n\r")
- prob_width = 66
- problem = problem.ljust(prob_width)[0 : (prob_width - 1)]
- text = line.replace("\n", "\n".ljust(prob_width + 22))
- message = "%s %s" % (problem, text)
- self.__add_msg(message)
-
-
-class UTF8Reader(BaseReader):
- """The main UTF-8 reader, uses Python for char handling"""
-
- def __init__(self, ifile, __add_msg, enc):
- BaseReader.__init__(self, ifile, enc, __add_msg)
- self.reset()
- if enc == "UTF_8_SIG":
- self.ifile = TextIOWrapper(
- ifile, encoding="utf_8_sig", errors="replace", newline=None
- )
- else:
- self.ifile = TextIOWrapper(
- ifile, encoding="utf_8", errors="replace", newline=None
- )
-
- def readline(self):
- line = self.ifile.readline()
- return line.translate(STRIP_DICT)
-
-
-class UTF16Reader(BaseReader):
- """The main UTF-16 reader, uses Python for char handling"""
-
- def __init__(self, ifile, __add_msg):
- BaseReader.__init__(self, ifile, "UTF16", __add_msg)
- self.ifile = TextIOWrapper(
- ifile, encoding="utf_16", errors="replace", newline=None
- )
- self.reset()
-
- def readline(self):
- line = self.ifile.readline()
- return line.translate(STRIP_DICT)
-
-
-class AnsiReader(BaseReader):
- """The main ANSI (latin1) reader, uses Python for char handling"""
-
- def __init__(self, ifile, __add_msg):
- BaseReader.__init__(self, ifile, "latin1", __add_msg)
- self.ifile = TextIOWrapper(
- ifile, encoding="latin1", errors="replace", newline=None
- )
-
- def readline(self):
- line = self.ifile.readline()
- if line.translate(DEL_AND_C1) != line:
- self.report_error(
- "DEL or C1 control chars in line did you mean " "CHAR cp1252??", line
- )
- return line.translate(STRIP_DICT)
-
-
-class CP1252Reader(BaseReader):
- """The extra credit CP1252 reader, uses Python for char handling"""
-
- def __init__(self, ifile, __add_msg):
- BaseReader.__init__(self, ifile, "cp1252", __add_msg)
- self.ifile = TextIOWrapper(
- ifile, encoding="cp1252", errors="replace", newline=None
- )
-
- def readline(self):
- line = self.ifile.readline()
- return line.translate(STRIP_DICT)
-
-
-class AnselReader(BaseReader):
- """
- ANSEL to Unicode Conversion
-
- ANSEL references:
- http://lcweb2.loc.gov/diglib/codetables/45.html
- http://www.gymel.com/charsets/ANSEL.html
-
- list of ANSEL codes that replicate ASCII
- note that DEL (127=0x7F) is a control char
- Note: spec allows control-chars that Gramps probably doesn't use
- but 10=0x0A _is_ needed (!)
- ---
- Also: there are two additional control chars 0x98,0x9c (unicode same)
- which we also ignore for now (start/emd of string (or sort sequence)
- ---
- ?: should we allow TAB, as a Gramps extension?
- """
-
- __printable_ascii = list(map(chr, list(range(32, 127)))) # up thru 126
- # LF CR Esc GS RS US
- __use_ASCII = list(map(chr, [10, 13, 27, 29, 30, 31])) + __printable_ascii
-
- # mappings of single byte ANSEL codes to unicode
- __onebyte = {
- b"\xa1": "\u0141",
- b"\xa2": "\u00d8",
- b"\xa3": "\u0110",
- b"\xa4": "\u00de",
- b"\xa5": "\u00c6",
- b"\xa6": "\u0152",
- b"\xa7": "\u02b9",
- b"\xa8": "\u00b7",
- b"\xa9": "\u266d",
- b"\xaa": "\u00ae",
- b"\xab": "\u00b1",
- b"\xac": "\u01a0",
- b"\xad": "\u01af",
- b"\xae": "\u02bc",
- b"\xb0": "\u02bb",
- b"\xb1": "\u0142",
- b"\xb2": "\u00f8",
- b"\xb3": "\u0111",
- b"\xb4": "\u00fe",
- b"\xb5": "\u00e6",
- b"\xb6": "\u0153",
- b"\xb7": "\u02ba",
- b"\xb8": "\u0131",
- b"\xb9": "\u00a3",
- b"\xba": "\u00f0",
- b"\xbc": "\u01a1",
- b"\xbd": "\u01b0",
- b"\xbe": "\u25a1",
- b"\xbf": "\u25a0",
- b"\xc0": "\u00b0",
- b"\xc1": "\u2113",
- b"\xc2": "\u2117",
- b"\xc3": "\u00a9",
- b"\xc4": "\u266f",
- b"\xc5": "\u00bf",
- b"\xc6": "\u00a1",
- b"\xc7": "\u00df",
- b"\xc8": "\u20ac",
- b"\xcd": "\u0065",
- b"\xce": "\u006f",
- b"\xcf": "\u00df",
- }
-
- # combining forms (in ANSEL, they precede the modified ASCII character
- # whereas the unicode combining term follows the character modified
- # Note: unicode allows multiple modifiers, but ANSEL may not (TDB?),
- # so we ignore multiple combining forms in this module
- # 8d & 8e are zero-width joiner (ZWJ), and zero-width non-joiner ZWNJ
- # (strange things) probably not commonly found in our needs, unless one
- # starts writing persian (or???) poetry in ANSEL
- __acombiners = {
- b"\x8d": "\u200d",
- b"\x8e": "\u200c",
- b"\xe0": "\u0309",
- b"\xe1": "\u0300",
- b"\xe2": "\u0301",
- b"\xe3": "\u0302",
- b"\xe4": "\u0303",
- b"\xe5": "\u0304",
- b"\xe6": "\u0306",
- b"\xe7": "\u0307",
- b"\xe8": "\u0308",
- b"\xe9": "\u030c",
- b"\xea": "\u030a",
- b"\xeb": "\ufe20",
- b"\xec": "\ufe21",
- b"\xed": "\u0315",
- b"\xee": "\u030b",
- b"\xef": "\u0310",
- b"\xf0": "\u0327",
- b"\xf1": "\u0328",
- b"\xf2": "\u0323",
- b"\xf3": "\u0324",
- b"\xf4": "\u0325",
- b"\xf5": "\u0333",
- b"\xf6": "\u0332",
- b"\xf7": "\u0326",
- b"\xf8": "\u031c",
- b"\xf9": "\u032e",
- b"\xfa": "\ufe22",
- b"\xfb": "\ufe23",
- b"\xfc": "\u0338",
- b"\xfe": "\u0313",
- }
-
- # mappings of two byte (precomposed forms) ANSEL codes to unicode
- __twobyte = {
- b"\xe0\x41": "\u1ea2",
- b"\xe0\x45": "\u1eba",
- b"\xe0\x49": "\u1ec8",
- b"\xe0\x4f": "\u1ece",
- b"\xe0\x55": "\u1ee6",
- b"\xe0\x59": "\u1ef6",
- b"\xe0\x61": "\u1ea3",
- b"\xe0\x65": "\u1ebb",
- b"\xe0\x69": "\u1ec9",
- b"\xe0\x6f": "\u1ecf",
- b"\xe0\x75": "\u1ee7",
- b"\xe0\x79": "\u1ef7",
- b"\xe1\x41": "\u00c0",
- b"\xe1\x45": "\u00c8",
- b"\xe1\x49": "\u00cc",
- b"\xe1\x4f": "\u00d2",
- b"\xe1\x55": "\u00d9",
- b"\xe1\x57": "\u1e80",
- b"\xe1\x59": "\u1ef2",
- b"\xe1\x61": "\u00e0",
- b"\xe1\x65": "\u00e8",
- b"\xe1\x69": "\u00ec",
- b"\xe1\x6f": "\u00f2",
- b"\xe1\x75": "\u00f9",
- b"\xe1\x77": "\u1e81",
- b"\xe1\x79": "\u1ef3",
- b"\xe2\x41": "\u00c1",
- b"\xe2\x43": "\u0106",
- b"\xe2\x45": "\u00c9",
- b"\xe2\x47": "\u01f4",
- b"\xe2\x49": "\u00cd",
- b"\xe2\x4b": "\u1e30",
- b"\xe2\x4c": "\u0139",
- b"\xe2\x4d": "\u1e3e",
- b"\xe2\x4e": "\u0143",
- b"\xe2\x4f": "\u00d3",
- b"\xe2\x50": "\u1e54",
- b"\xe2\x52": "\u0154",
- b"\xe2\x53": "\u015a",
- b"\xe2\x55": "\u00da",
- b"\xe2\x57": "\u1e82",
- b"\xe2\x59": "\u00dd",
- b"\xe2\x5a": "\u0179",
- b"\xe2\x61": "\u00e1",
- b"\xe2\x63": "\u0107",
- b"\xe2\x65": "\u00e9",
- b"\xe2\x67": "\u01f5",
- b"\xe2\x69": "\u00ed",
- b"\xe2\x6b": "\u1e31",
- b"\xe2\x6c": "\u013a",
- b"\xe2\x6d": "\u1e3f",
- b"\xe2\x6e": "\u0144",
- b"\xe2\x6f": "\u00f3",
- b"\xe2\x70": "\u1e55",
- b"\xe2\x72": "\u0155",
- b"\xe2\x73": "\u015b",
- b"\xe2\x75": "\u00fa",
- b"\xe2\x77": "\u1e83",
- b"\xe2\x79": "\u00fd",
- b"\xe2\x7a": "\u017a",
- b"\xe2\xa5": "\u01fc",
- b"\xe2\xb5": "\u01fd",
- b"\xe3\x41": "\u00c2",
- b"\xe3\x43": "\u0108",
- b"\xe3\x45": "\u00ca",
- b"\xe3\x47": "\u011c",
- b"\xe3\x48": "\u0124",
- b"\xe3\x49": "\u00ce",
- b"\xe3\x4a": "\u0134",
- b"\xe3\x4f": "\u00d4",
- b"\xe3\x53": "\u015c",
- b"\xe3\x55": "\u00db",
- b"\xe3\x57": "\u0174",
- b"\xe3\x59": "\u0176",
- b"\xe3\x5a": "\u1e90",
- b"\xe3\x61": "\u00e2",
- b"\xe3\x63": "\u0109",
- b"\xe3\x65": "\u00ea",
- b"\xe3\x67": "\u011d",
- b"\xe3\x68": "\u0125",
- b"\xe3\x69": "\u00ee",
- b"\xe3\x6a": "\u0135",
- b"\xe3\x6f": "\u00f4",
- b"\xe3\x73": "\u015d",
- b"\xe3\x75": "\u00fb",
- b"\xe3\x77": "\u0175",
- b"\xe3\x79": "\u0177",
- b"\xe3\x7a": "\u1e91",
- b"\xe4\x41": "\u00c3",
- b"\xe4\x45": "\u1ebc",
- b"\xe4\x49": "\u0128",
- b"\xe4\x4e": "\u00d1",
- b"\xe4\x4f": "\u00d5",
- b"\xe4\x55": "\u0168",
- b"\xe4\x56": "\u1e7c",
- b"\xe4\x59": "\u1ef8",
- b"\xe4\x61": "\u00e3",
- b"\xe4\x65": "\u1ebd",
- b"\xe4\x69": "\u0129",
- b"\xe4\x6e": "\u00f1",
- b"\xe4\x6f": "\u00f5",
- b"\xe4\x75": "\u0169",
- b"\xe4\x76": "\u1e7d",
- b"\xe4\x79": "\u1ef9",
- b"\xe5\x41": "\u0100",
- b"\xe5\x45": "\u0112",
- b"\xe5\x47": "\u1e20",
- b"\xe5\x49": "\u012a",
- b"\xe5\x4f": "\u014c",
- b"\xe5\x55": "\u016a",
- b"\xe5\x61": "\u0101",
- b"\xe5\x65": "\u0113",
- b"\xe5\x67": "\u1e21",
- b"\xe5\x69": "\u012b",
- b"\xe5\x6f": "\u014d",
- b"\xe5\x75": "\u016b",
- b"\xe5\xa5": "\u01e2",
- b"\xe5\xb5": "\u01e3",
- b"\xe6\x41": "\u0102",
- b"\xe6\x45": "\u0114",
- b"\xe6\x47": "\u011e",
- b"\xe6\x49": "\u012c",
- b"\xe6\x4f": "\u014e",
- b"\xe6\x55": "\u016c",
- b"\xe6\x61": "\u0103",
- b"\xe6\x65": "\u0115",
- b"\xe6\x67": "\u011f",
- b"\xe6\x69": "\u012d",
- b"\xe6\x6f": "\u014f",
- b"\xe6\x75": "\u016d",
- b"\xe7\x42": "\u1e02",
- b"\xe7\x43": "\u010a",
- b"\xe7\x44": "\u1e0a",
- b"\xe7\x45": "\u0116",
- b"\xe7\x46": "\u1e1e",
- b"\xe7\x47": "\u0120",
- b"\xe7\x48": "\u1e22",
- b"\xe7\x49": "\u0130",
- b"\xe7\x4d": "\u1e40",
- b"\xe7\x4e": "\u1e44",
- b"\xe7\x50": "\u1e56",
- b"\xe7\x52": "\u1e58",
- b"\xe7\x53": "\u1e60",
- b"\xe7\x54": "\u1e6a",
- b"\xe7\x57": "\u1e86",
- b"\xe7\x58": "\u1e8a",
- b"\xe7\x59": "\u1e8e",
- b"\xe7\x5a": "\u017b",
- b"\xe7\x62": "\u1e03",
- b"\xe7\x63": "\u010b",
- b"\xe7\x64": "\u1e0b",
- b"\xe7\x65": "\u0117",
- b"\xe7\x66": "\u1e1f",
- b"\xe7\x67": "\u0121",
- b"\xe7\x68": "\u1e23",
- b"\xe7\x6d": "\u1e41",
- b"\xe7\x6e": "\u1e45",
- b"\xe7\x70": "\u1e57",
- b"\xe7\x72": "\u1e59",
- b"\xe7\x73": "\u1e61",
- b"\xe7\x74": "\u1e6b",
- b"\xe7\x77": "\u1e87",
- b"\xe7\x78": "\u1e8b",
- b"\xe7\x79": "\u1e8f",
- b"\xe7\x7a": "\u017c",
- b"\xe8\x41": "\u00c4",
- b"\xe8\x45": "\u00cb",
- b"\xe8\x48": "\u1e26",
- b"\xe8\x49": "\u00cf",
- b"\xe8\x4f": "\u00d6",
- b"\xe8\x55": "\u00dc",
- b"\xe8\x57": "\u1e84",
- b"\xe8\x58": "\u1e8c",
- b"\xe8\x59": "\u0178",
- b"\xe8\x61": "\u00e4",
- b"\xe8\x65": "\u00eb",
- b"\xe8\x68": "\u1e27",
- b"\xe8\x69": "\u00ef",
- b"\xe8\x6f": "\u00f6",
- b"\xe8\x74": "\u1e97",
- b"\xe8\x75": "\u00fc",
- b"\xe8\x77": "\u1e85",
- b"\xe8\x78": "\u1e8d",
- b"\xe8\x79": "\u00ff",
- b"\xe9\x41": "\u01cd",
- b"\xe9\x43": "\u010c",
- b"\xe9\x44": "\u010e",
- b"\xe9\x45": "\u011a",
- b"\xe9\x47": "\u01e6",
- b"\xe9\x49": "\u01cf",
- b"\xe9\x4b": "\u01e8",
- b"\xe9\x4c": "\u013d",
- b"\xe9\x4e": "\u0147",
- b"\xe9\x4f": "\u01d1",
- b"\xe9\x52": "\u0158",
- b"\xe9\x53": "\u0160",
- b"\xe9\x54": "\u0164",
- b"\xe9\x55": "\u01d3",
- b"\xe9\x5a": "\u017d",
- b"\xe9\x61": "\u01ce",
- b"\xe9\x63": "\u010d",
- b"\xe9\x64": "\u010f",
- b"\xe9\x65": "\u011b",
- b"\xe9\x67": "\u01e7",
- b"\xe9\x69": "\u01d0",
- b"\xe9\x6a": "\u01f0",
- b"\xe9\x6b": "\u01e9",
- b"\xe9\x6c": "\u013e",
- b"\xe9\x6e": "\u0148",
- b"\xe9\x6f": "\u01d2",
- b"\xe9\x72": "\u0159",
- b"\xe9\x73": "\u0161",
- b"\xe9\x74": "\u0165",
- b"\xe9\x75": "\u01d4",
- b"\xe9\x7a": "\u017e",
- b"\xea\x41": "\u00c5",
- b"\xea\x61": "\u00e5",
- b"\xea\x75": "\u016f",
- b"\xea\x77": "\u1e98",
- b"\xea\x79": "\u1e99",
- b"\xea\xad": "\u016e",
- b"\xee\x4f": "\u0150",
- b"\xee\x55": "\u0170",
- b"\xee\x6f": "\u0151",
- b"\xee\x75": "\u0171",
- b"\xf0\x20": "\u00b8",
- b"\xf0\x43": "\u00c7",
- b"\xf0\x44": "\u1e10",
- b"\xf0\x47": "\u0122",
- b"\xf0\x48": "\u1e28",
- b"\xf0\x4b": "\u0136",
- b"\xf0\x4c": "\u013b",
- b"\xf0\x4e": "\u0145",
- b"\xf0\x52": "\u0156",
- b"\xf0\x53": "\u015e",
- b"\xf0\x54": "\u0162",
- b"\xf0\x63": "\u00e7",
- b"\xf0\x64": "\u1e11",
- b"\xf0\x67": "\u0123",
- b"\xf0\x68": "\u1e29",
- b"\xf0\x6b": "\u0137",
- b"\xf0\x6c": "\u013c",
- b"\xf0\x6e": "\u0146",
- b"\xf0\x72": "\u0157",
- b"\xf0\x73": "\u015f",
- b"\xf0\x74": "\u0163",
- b"\xf1\x41": "\u0104",
- b"\xf1\x45": "\u0118",
- b"\xf1\x49": "\u012e",
- b"\xf1\x4f": "\u01ea",
- b"\xf1\x55": "\u0172",
- b"\xf1\x61": "\u0105",
- b"\xf1\x65": "\u0119",
- b"\xf1\x69": "\u012f",
- b"\xf1\x6f": "\u01eb",
- b"\xf1\x75": "\u0173",
- b"\xf2\x41": "\u1ea0",
- b"\xf2\x42": "\u1e04",
- b"\xf2\x44": "\u1e0c",
- b"\xf2\x45": "\u1eb8",
- b"\xf2\x48": "\u1e24",
- b"\xf2\x49": "\u1eca",
- b"\xf2\x4b": "\u1e32",
- b"\xf2\x4c": "\u1e36",
- b"\xf2\x4d": "\u1e42",
- b"\xf2\x4e": "\u1e46",
- b"\xf2\x4f": "\u1ecc",
- b"\xf2\x52": "\u1e5a",
- b"\xf2\x53": "\u1e62",
- b"\xf2\x54": "\u1e6c",
- b"\xf2\x55": "\u1ee4",
- b"\xf2\x56": "\u1e7e",
- b"\xf2\x57": "\u1e88",
- b"\xf2\x59": "\u1ef4",
- b"\xf2\x5a": "\u1e92",
- b"\xf2\x61": "\u1ea1",
- b"\xf2\x62": "\u1e05",
- b"\xf2\x64": "\u1e0d",
- b"\xf2\x65": "\u1eb9",
- b"\xf2\x68": "\u1e25",
- b"\xf2\x69": "\u1ecb",
- b"\xf2\x6b": "\u1e33",
- b"\xf2\x6c": "\u1e37",
- b"\xf2\x6d": "\u1e43",
- b"\xf2\x6e": "\u1e47",
- b"\xf2\x6f": "\u1ecd",
- b"\xf2\x72": "\u1e5b",
- b"\xf2\x73": "\u1e63",
- b"\xf2\x74": "\u1e6d",
- b"\xf2\x75": "\u1ee5",
- b"\xf2\x76": "\u1e7f",
- b"\xf2\x77": "\u1e89",
- b"\xf2\x79": "\u1ef5",
- b"\xf2\x7a": "\u1e93",
- b"\xf3\x55": "\u1e72",
- b"\xf3\x75": "\u1e73",
- b"\xf4\x41": "\u1e00",
- b"\xf4\x61": "\u1e01",
- b"\xf9\x48": "\u1e2a",
- b"\xf9\x68": "\u1e2b",
- }
-
- def __ansel_to_unicode(self, text):
- """Convert an ANSEL encoded text to unicode"""
-
- buff = StringIO()
- error = ""
- while text:
- if text[0] < 128:
- if chr(text[0]) in AnselReader.__use_ASCII:
- head = chr(text[0])
- else:
- # substitute space for disallowed (control) chars
- error += " (%#X)" % text[0]
- head = " "
- text = text[1:]
- else:
- if text[0:2] in AnselReader.__twobyte:
- head = AnselReader.__twobyte[text[0:2]]
- text = text[2:]
- elif bytes([text[0]]) in AnselReader.__onebyte:
- head = AnselReader.__onebyte[bytes([text[0]])]
- text = text[1:]
- elif bytes([text[0]]) in AnselReader.__acombiners:
- cmb = AnselReader.__acombiners[bytes([text[0]])]
- # always consume the combiner
- text = text[1:]
- next_byte = text[0]
- if (
- next_byte < 128
- and chr(next_byte) in AnselReader.__printable_ascii
- ):
- # consume next as well
- text = text[1:]
- # unicode: combiner follows base-char
- head = chr(next_byte) + cmb
- else:
- # just drop the unexpected combiner
- error += " (%#X)" % text[0]
- continue
- else:
- error += " (%#X)" % text[0]
- head = "\ufffd" # "Replacement Char"
- text = text[1:]
- buff.write(head)
- ans = buff.getvalue()
-
- if error:
- # e.g. Illegal character (oxAB) (0xCB)... 1 NOTE xyz?pqr?lmn
- self.report_error(_("Illegal character%s") % error, ans)
- buff.close()
- return ans
-
- def __init__(self, ifile, __add_msg):
- BaseReader.__init__(self, ifile, "ANSEL", __add_msg)
- # In theory, we should have been able to skip the encode/decode from
- # ascii. But this way allows us to use pythons universal newline
- self.ifile = TextIOWrapper(
- ifile, encoding="ascii", errors="surrogateescape", newline=None
- )
-
- def readline(self):
- line = self.ifile.readline()
- linebytes = line.encode(encoding="ascii", errors="surrogateescape")
- return self.__ansel_to_unicode(linebytes)
-
-
-# -------------------------------------------------------------------------
-#
-# CurrentState
-#
-# -------------------------------------------------------------------------
-class CurrentState:
- """
- Keep track of the current state variables.
- """
-
- def __init__(self, person=None, level=0, event=None, event_ref=None):
- """
- Initialize the object.
- """
- self.name_cnt = 0
- self.person = person
- self.family = None
- self.level = level
- self.event = event
- self.event_ref = event_ref
- self.source_ref = None
- self.citation = None
- self.note = None
- self.lds_ord = None
- self.msg = ""
- self.primary = False # _PRIMARY tag on an INDI.FAMC tag
- self.filename = ""
- self.title = ""
- self.addr = None
- self.res = None
- self.source = None
- self.ftype = None
- self.pf = None # method for parsing places
- self.location = None
- self.place_fields = None # method for parsing places
- self.ref = None # PersonRef
- self.handle = None #
- self.form = "" # Multimedia format
- self.frel = None # Child relation to father
- self.mrel = None
- self.repo = None
- self.attr = None
- self.obj = None
- self.name = ""
- self.ignore = False
- self.repo_ref = None
- self.place = None
- self.media = None
- self.photo = "" # Person primary photo
- self.prim = None # Photo is primary
-
- def __getattr__(self, name):
- """
- Return the value associated with the specified attribute.
- """
- return self.__dict__.get(name)
-
- def __setattr__(self, name, value):
- """
- Set the value associated with the specified attribute.
- """
- self.__dict__[name] = value
-
-
-# -------------------------------------------------------------------------
-#
-# PlaceParser
-#
-# -------------------------------------------------------------------------
-class PlaceParser:
- """
- Provide the ability to parse GEDCOM FORM statements for places, and
- the parse the line of text, mapping the text components to Location
- values based of the FORM statement.
- """
-
- __field_map = {
- "addr": Location.set_street,
- "subdivision": Location.set_street,
- "addr1": Location.set_street,
- "adr1": Location.set_street,
- "street": Location.set_street,
- "addr2": Location.set_locality,
- "adr2": Location.set_locality,
- "locality": Location.set_locality,
- "neighborhood": Location.set_locality,
- "city": Location.set_city,
- "town": Location.set_city,
- "village": Location.set_city,
- "county": Location.set_county,
- "country": Location.set_country,
- "state": Location.set_state,
- "state/province": Location.set_state,
- "region": Location.set_state,
- "province": Location.set_state,
- "area code": Location.set_postal_code,
- "post code": Location.set_postal_code,
- "zip code": Location.set_postal_code,
- }
-
- def __init__(self, line=None):
- self.parse_function = []
-
- if line:
- self.parse_form(line)
-
- def parse_form(self, line):
- """
- Parses the GEDCOM PLAC.FORM into a list of function
- pointers (if possible). It does this my mapping the text strings
- (separated by commas) to the corresponding Location
- method via the __field_map variable
- """
- for item in line.data.split(","):
- item = item.lower().strip()
- fcn = self.__field_map.get(item, lambda x, y: None)
- self.parse_function.append(fcn)
-
- def load_place(self, place_import, place, text):
- """
- Takes the text string representing a place, splits it into
- its subcomponents (comma separated), and calls the approriate
- function based of its position, depending on the parsed value
- from the FORM statement.
- """
- items = [item.strip() for item in text.split(",")]
- if len(items) != len(self.parse_function):
- return
- index = 0
- loc = Location()
- for item in items:
- self.parse_function[index](loc, item)
- index += 1
-
- location = (
- loc.get_street(),
- loc.get_locality(),
- loc.get_parish(),
- loc.get_city(),
- loc.get_county(),
- loc.get_state(),
- loc.get_country(),
- )
-
- for level, name in enumerate(location):
- if name:
- break
-
- if name:
- type_num = 7 - level
- else:
- name = place.title
- type_num = PlaceType.UNKNOWN
- place.name.set_value(name)
- place.set_type(PlaceType(type_num))
- code = loc.get_postal_code()
- place.set_code(code)
- if place.handle: # if handle is available, store immediately
- place_import.store_location(location, place.handle)
- else: # return for storage later
- return location
-
-
-# -------------------------------------------------------------------------
-#
-# IdFinder
-#
-# -------------------------------------------------------------------------
-class IdFinder:
- """
- Provide method of finding the next available ID.
- """
-
- def __init__(self, keys, prefix):
- """
- Initialize the object.
- """
- self.ids = set(keys)
- self.index = 0
- self.prefix = prefix
-
- def find_next(self):
- """
- Return the next available GRAMPS' ID for a Event object based
- off the person ID prefix.
-
- @return: Returns the next available index
- @rtype: str
- """
- index = self.prefix % self.index
- while index in self.ids:
- self.index += 1
- index = self.prefix % self.index
- self.ids.add(index)
- self.index += 1
- return index
-
-
-# -------------------------------------------------------------------------
-#
-# IdMapper
-#
-# -------------------------------------------------------------------------
-class IdMapper:
- """This class provide methods to keep track of the correspoindence between
- Gedcom xrefs (@P1023@) and Gramps IDs."""
-
- def __init__(self, has_gid, find_next, id2user_format):
- self.has_gid = has_gid
- self.find_next = find_next
- self.id2user_format = id2user_format
- self.swap = {}
-
- def __getitem__(self, gid):
- if gid == "":
- # We need to find the next gramps ID provided it is not already
- # the target of a swap
- new_val = self.find_next()
- while new_val in list(self.swap.values()):
- new_val = self.find_next()
- else:
- # remove any @ signs
- gid = self.clean(gid)
- if gid in self.swap:
- return self.swap[gid]
- else:
- # now standardise the format
- formatted_gid = self.id2user_format(gid)
- # I1 and I0001 will both format as I0001. If we had already
- # encountered I1, it would be in self.swap, so we would already
- # have found it. If we had already encountered I0001 and we are
- # now looking for I1, it wouldn't be in self.swap, and we now
- # find that I0001 is in use, so we have to create a new id.
- if self.has_gid(formatted_gid) or (
- formatted_gid in list(self.swap.values())
- ):
- new_val = self.find_next()
- while new_val in list(self.swap.values()):
- new_val = self.find_next()
- else:
- new_val = formatted_gid
- # we need to distinguish between I1 and I0001, so we record the map
- # from the original format
- self.swap[gid] = new_val
- return new_val
-
- def clean(self, gid):
- """remove '@' from start and end of xref"""
- temp = gid.strip()
- if len(temp) > 1 and temp[0] == "@" and temp[-1] == "@":
- temp = temp[1:-1]
- return temp
-
- def map(self):
- """return the xref to GID translation map"""
- return self.swap
-
-
-# -------------------------------------------------------------------------
-#
-# GedcomParser
-#
-# -------------------------------------------------------------------------
-class GedcomParser(UpdateCallback):
- """
- Performs the second pass of the GEDCOM parser, which does all the heavy
- lifting.
- """
-
- __TRUNC_MSG = _(
- "Your GEDCOM file is corrupted. " "It appears to have been truncated."
- )
- _EMPTY_LOC = object_to_dict(Location())
-
- SyntaxError = "Syntax Error"
- BadFile = "Not a GEDCOM file"
-
- @staticmethod
- def __find_from_handle(gramps_id, table):
- """
- Find a handle corresponding to the specified Gramps ID.
-
- The passed table contains the mapping. If the value is found, we return
- it, otherwise we create a new handle, store it, and return it.
-
- """
- intid = table.get(gramps_id)
- if not intid:
- intid = create_id()
- table[gramps_id] = intid
- return intid
-
- @staticmethod
- def __parse_name_personal(text):
- """
- Parses a GEDCOM NAME value into an Name structure
- """
- name = Name()
-
- match = SURNAME_RE.match(text)
- if match:
- # /surname/ extra, we assume extra is given name
- names = match.groups()
- name.set_first_name(names[1].strip())
- surn = Surname()
- surn.set_surname(names[0].strip())
- surn.set_primary()
- name.set_surname_list([surn])
- else:
- try:
- names = NAME_RE.match(text).groups()
- # given /surname/ extra, we assume extra is suffix
- name.set_first_name(names[0].strip())
- surn = Surname()
- surn.set_surname(names[2].strip())
- surn.set_primary()
- name.set_surname_list([surn])
- name.set_suffix(names[4].strip())
- except:
- # something strange, set as first name
- name.set_first_name(text.strip())
- return name
-
- def __init__(
- self,
- dbase,
- ifile,
- filename,
- user,
- stage_one,
- default_source,
- default_tag_format=None,
- ):
- UpdateCallback.__init__(self, user.callback)
- self.user = user
- self.set_total(stage_one.get_line_count())
- self.repo2id = {}
- self.trans = None
- self.errors = []
- self.number_of_errors = 0
- self.maxpeople = stage_one.get_person_count()
- self.dbase = dbase
- self.import_researcher = self.dbase.get_total() == 0
- event_ids = []
- for event in dbase.iter_events():
- event_ids.append(event.gramps_id)
- self.emapper = IdFinder(event_ids, dbase.event_prefix)
- self.famc_map = stage_one.get_famc_map()
- self.fams_map = stage_one.get_fams_map()
-
- self.place_parser = PlaceParser()
- self.inline_srcs = OrderedDict()
- self.media_map = {}
- self.note_type_map = {}
- self.genby = ""
- self.genvers = ""
- self.subm = ""
- self.use_def_src = default_source
- self.func_list = []
- if self.use_def_src:
- self.def_src = Source()
- fname = os.path.basename(filename).split("\\")[-1]
- self.def_src.set_title(_("Import from GEDCOM (%s)") % fname)
- if default_tag_format:
- name = time.strftime(default_tag_format)
- tag = self.dbase.get_tag_from_name(name)
- if tag:
- self.default_tag = tag
- else:
- self.default_tag = Tag()
- self.default_tag.set_name(name)
- else:
- self.default_tag = None
- self.dir_path = os.path.dirname(filename)
- self.is_ftw = False
- self.addr_is_detail = False
- self.groups = None
- self.want_parse_warnings = True
-
- self.pid_map = IdMapper(
- self.dbase.has_person_gramps_id,
- self.dbase.find_next_person_gramps_id,
- self.dbase.id2user_format,
- )
- self.fid_map = IdMapper(
- self.dbase.has_family_gramps_id,
- self.dbase.find_next_family_gramps_id,
- self.dbase.fid2user_format,
- )
- self.sid_map = IdMapper(
- self.dbase.has_source_gramps_id,
- self.dbase.find_next_source_gramps_id,
- self.dbase.sid2user_format,
- )
- self.oid_map = IdMapper(
- self.dbase.has_media_gramps_id,
- self.dbase.find_next_media_gramps_id,
- self.dbase.oid2user_format,
- )
- self.rid_map = IdMapper(
- self.dbase.has_repository_gramps_id,
- self.dbase.find_next_repository_gramps_id,
- self.dbase.rid2user_format,
- )
- self.nid_map = IdMapper(
- self.dbase.has_note_gramps_id,
- self.dbase.find_next_note_gramps_id,
- self.dbase.nid2user_format,
- )
-
- self.gid2id = {}
- self.oid2id = {}
- self.sid2id = {}
- self.lid2id = {}
- self.fid2id = {}
- self.rid2id = {}
- self.nid2id = {}
-
- self.place_import = PlaceImport(self.dbase)
-
- #
- # Parse table for <> below the level 0 SUBM tag
- #
- # n @@ SUBM {1:1}
- # +1 NAME {1:1}
- # +1 <> {0:1}
- # +1 <> {0:M}
- # +1 LANG {0:3}
- # +1 <> {0:M}
- # +1 RFN {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:1}
-
- # (N.B. GEDCOM allows multiple SUBMitter records)
- self.subm_parse_tbl = {
- # +1 NAME
- TOKEN_NAME: self.__subm_name,
- # +1 <>
- TOKEN_ADDR: self.__subm_addr,
- TOKEN_PHON: self.__subm_phon,
- TOKEN_EMAIL: self.__subm_email,
- TOKEN_WWW: self.__repo_www,
- TOKEN_FAX: self.__repo_fax,
- # +1 <>
- # +1 LANG
- # +1 <>
- TOKEN_NOTE: self.__repo_note,
- TOKEN_RNOTE: self.__repo_note,
- # +1 RFN
- # +1 RIN
- # +1 <>
- TOKEN_CHAN: self.__repo_chan,
- }
- self.func_list.append(self.subm_parse_tbl)
-
- #
- # Parse table for <> below the level 0 INDI tag
- #
- # n @@ INDI {1:1}
- # +1 RESN {0:1}
- # +1 <> {0:M}
- # +1 SEX {0:1}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 SUBM @@ {0:M}
- # +1 <> {0:M}
- # +1 ALIA @@ {0:M}
- # +1 ANCI @@ {0:M}
- # +1 DESI @@ {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 RFN {0:1}
- # +1 AFN {0:1}
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:1}
-
- self.indi_parse_tbl = {
- # +1 RESN {0:1}
- TOKEN_RESN: self.__person_resn,
- # +1 <> {0:M}
- TOKEN_NAME: self.__person_name,
- # +1 SEX {0:1}
- TOKEN_SEX: self.__person_sex,
- # +1 <> {0:M}
- TOKEN_EVEN: self.__person_even,
- TOKEN_GEVENT: self.__person_std_event,
- TOKEN_BIRT: self.__person_birt,
- TOKEN_RELI: self.__person_reli,
- TOKEN_ADOP: self.__person_adop,
- TOKEN_DEAT: self.__person_deat,
- # +1 <> {0:M}
- # +1 AFN {0:1}
- TOKEN_ATTR: self.__person_std_attr,
- TOKEN_FACT: self.__person_fact,
- # +1 <> {0:M}
- TOKEN_BAPL: self.__person_bapl,
- TOKEN_CONL: self.__person_conl,
- TOKEN_INIL: self.__person_inil,
- TOKEN_ENDL: self.__person_endl,
- TOKEN_SLGC: self.__person_slgc,
- # +1 <> {0:M}
- TOKEN_FAMC: self.__person_famc,
- # +1 <> {0:M}
- TOKEN_FAMS: self.__person_fams,
- # +1 SUBM @@ {0:M}
- TOKEN_SUBM: self.__skip_record,
- # +1 <> {0:M}
- TOKEN_ASSO: self.__person_asso,
- # +1 ALIA @@ {0:M}
- TOKEN_ALIA: self.__person_alt_name,
- # +1 ANCI @@ {0:M}
- TOKEN_ANCI: self.__skip_record,
- # +1 DESI @@ {0:M}
- TOKEN_DESI: self.__skip_record,
- # +1 <> {0:M}
- TOKEN_SOUR: self.__person_sour,
- # +1 <> {0:M}
- TOKEN_OBJE: self.__person_object,
- # +1 <> {0:M}
- TOKEN_NOTE: self.__person_note,
- TOKEN_RNOTE: self.__person_note,
- TOKEN__COMM: self.__person_note,
- # +1 RFN {0:1}
- TOKEN_RFN: self.__person_attr,
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- TOKEN_REFN: self.__person_refn,
- # TYPE should be below REFN, but will work here anyway
- TOKEN_TYPE: self.__person_attr,
- # +1 RIN {0:1}
- TOKEN_RIN: self.__person_attr,
- # +1 <> {0:1}
- TOKEN_CHAN: self.__person_chan,
- # The following tags are not part of Gedcom spec but are commonly
- # found here anyway
- TOKEN_ADDR: self.__person_addr,
- TOKEN_PHON: self.__person_phon,
- TOKEN_FAX: self.__person_fax,
- TOKEN_EMAIL: self.__person_email,
- TOKEN_WWW: self.__person_www,
- TOKEN__TODO: self.__skip_record,
- TOKEN_TITL: self.__person_titl,
- TOKEN__PHOTO: self.__person_photo,
- }
- self.func_list.append(self.indi_parse_tbl)
-
- self.name_parse_tbl = {
- # +1 NPFX {0:1}
- TOKEN_NPFX: self.__name_npfx,
- # +1 GIVN {0:1}
- TOKEN_GIVN: self.__name_givn,
- # NICK {0:1}
- TOKEN_NICK: self.__name_nick,
- # _RUFNAME {0:1}
- TOKEN__CALLNAME: self.__name_call,
- # +1 SPFX {0:1}
- TOKEN_SURN: self.__name_surn,
- # +1 NSFX {0:1}
- TOKEN_NSFX: self.__name_nsfx,
- # +1 <> {0:M}
- TOKEN_SOUR: self.__name_sour,
- # +1 <> {0:M}
- TOKEN_NOTE: self.__name_note,
- TOKEN_RNOTE: self.__name_note,
- # Extensions
- TOKEN_ALIA: self.__name_alia,
- TOKEN__MARNM: self.__name_marnm,
- TOKEN__MAR: self.__name_marnm, # Generated by geni.com
- TOKEN__MARN: self.__name_marnm, # Gen'd by BROSKEEP 6.1.31 WIN
- TOKEN__AKA: self.__name_aka, # PAF and AncestQuest
- TOKEN_TYPE: self.__name_type, # This is legal GEDCOM 5.5.1
- TOKEN_BIRT: self.__ignore,
- TOKEN_DATE: self.__name_date,
- # This handles date as a subsidiary of "1 ALIA" which might be used
- # by Family Tree Maker and Reunion, and by cheating (handling a
- # lower level from the current parse table) handles date as
- # subsidiary to "2 _MARN", "2 _AKAN" and "2 _ADPN" which has been
- # found in Brother's keeper.
- TOKEN__ADPN: self.__name_adpn,
- }
- self.func_list.append(self.name_parse_tbl)
-
- #
- # Parse table for <> below the level 0 REPO tag
- #
- # n @@ REPO {1:1}
- # +1 NAME {0:1}
- # +1 <> {0:1}
- # +1 <> {0:M}
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:1}
-
- self.repo_parse_tbl = {
- TOKEN_NAME: self.__repo_name,
- TOKEN_ADDR: self.__repo_addr,
- TOKEN_RIN: self.__ignore,
- TOKEN_NOTE: self.__repo_note,
- TOKEN_RNOTE: self.__repo_note,
- TOKEN_CHAN: self.__repo_chan,
- TOKEN_PHON: self.__repo_phon,
- TOKEN_EMAIL: self.__repo_email,
- TOKEN_WWW: self.__repo_www,
- TOKEN_FAX: self.__repo_fax,
- }
- self.func_list.append(self.repo_parse_tbl)
-
- self.event_parse_tbl = {
- # n TYPE {0:1}
- TOKEN_TYPE: self.__event_type,
- # n DATE {0:1} p.*/*
- TOKEN_DATE: self.__event_date,
- # n <> {0:1} p.*
- TOKEN_PLAC: self.__event_place,
- # n <> {0:1} p.*
- TOKEN_ADDR: self.__event_addr,
- # n AGE {0:1} p.*
- TOKEN_AGE: self.__event_age,
- # n AGNC {0:1} p.*
- TOKEN_AGNC: self.__event_agnc,
- # n CAUS {0:1} p.*
- TOKEN_CAUS: self.__event_cause,
- # n <> {0:M} p.*
- TOKEN_SOUR: self.__event_source,
- # n <> {0:M} p.*, *
- TOKEN_OBJE: self.__event_object,
- # n <> {0:M} p.
- TOKEN_NOTE: self.__event_inline_note,
- TOKEN_RNOTE: self.__event_note,
- # Other
- TOKEN__PRIV: self.__event_privacy,
- TOKEN_OFFI: self.__event_note,
- TOKEN_PHON: self.__event_phon,
- TOKEN__GODP: self.__event_witness,
- TOKEN__WITN: self.__event_witness,
- TOKEN__WTN: self.__event_witness,
- TOKEN_RELI: self.__ignore,
- # Not legal, but inserted by PhpGedView
- TOKEN_TIME: self.__event_time,
- TOKEN_ASSO: self.__ignore,
- TOKEN_IGNORE: self.__ignore,
- TOKEN_STAT: self.__ignore,
- TOKEN_TEMP: self.__ignore,
- TOKEN_HUSB: self.__event_husb,
- TOKEN_WIFE: self.__event_wife,
- TOKEN_FAMC: self.__person_birth_famc,
- # Not legal, but inserted by Ultimate Family Tree
- TOKEN_CHAN: self.__ignore,
- TOKEN_QUAY: self.__ignore,
- # Not legal, but inserted by FamilyTreeBuilder
- TOKEN_RIN: self.__event_rin,
- TOKEN_ATTR: self.__event_attr, # FTB for _UID
- TOKEN_EMAIL: self.__event_email, # FTB for RESI events
- TOKEN_WWW: self.__event_www, # FTB for RESI events
- TOKEN_FAX: self.__event_fax, # legal...
- }
- self.func_list.append(self.event_parse_tbl)
-
- self.adopt_parse_tbl = {
- TOKEN_TYPE: self.__event_type,
- TOKEN__PRIV: self.__event_privacy,
- TOKEN_DATE: self.__event_date,
- TOKEN_SOUR: self.__event_source,
- TOKEN_PLAC: self.__event_place,
- TOKEN_ADDR: self.__event_addr,
- TOKEN_PHON: self.__event_phon,
- TOKEN_CAUS: self.__event_cause,
- TOKEN_AGNC: self.__event_agnc,
- TOKEN_AGE: self.__event_age,
- TOKEN_NOTE: self.__event_note,
- TOKEN_RNOTE: self.__event_note,
- TOKEN_OFFI: self.__event_note,
- TOKEN__GODP: self.__event_witness,
- TOKEN__WITN: self.__event_witness,
- TOKEN__WTN: self.__event_witness,
- TOKEN_RELI: self.__ignore,
- TOKEN_TIME: self.__ignore,
- TOKEN_ASSO: self.__ignore,
- TOKEN_IGNORE: self.__ignore,
- TOKEN_STAT: self.__ignore,
- TOKEN_TEMP: self.__ignore,
- TOKEN_OBJE: self.__event_object,
- TOKEN_FAMC: self.__person_adopt_famc,
- # Not legal, but inserted by Ultimate Family Tree
- TOKEN_CHAN: self.__ignore,
- TOKEN_QUAY: self.__ignore,
- }
- self.func_list.append(self.adopt_parse_tbl)
-
- self.famc_parse_tbl = {
- # n FAMC @@ {1:1}
- # +1 PEDI {0:1} p.*
- TOKEN_PEDI: self.__person_famc_pedi,
- # +1 _FREL {0:1} non-standard
- TOKEN__FREL: self.__person_famc_frel,
- # +1 _MREL {0:1} non-standard
- TOKEN__MREL: self.__person_famc_mrel,
- # +1 <> {0:M} p.*
- TOKEN_NOTE: self.__person_famc_note,
- TOKEN_RNOTE: self.__person_famc_note,
- # Extras
- TOKEN__PRIMARY: self.__person_famc_primary,
- TOKEN_SOUR: self.__person_famc_sour,
- # GEDit
- TOKEN_STAT: self.__ignore,
- }
- self.func_list.append(self.famc_parse_tbl)
-
- self.person_fact_parse_tbl = {
- TOKEN_TYPE: self.__person_fact_type,
- TOKEN_SOUR: self.__person_attr_source,
- TOKEN_NOTE: self.__person_attr_note,
- TOKEN_RNOTE: self.__person_attr_note,
- }
- self.func_list.append(self.person_fact_parse_tbl)
-
- self.person_attr_parse_tbl = {
- TOKEN_TYPE: self.__person_attr_type,
- TOKEN_CAUS: self.__ignore,
- TOKEN_DATE: self.__ignore,
- TOKEN_TIME: self.__ignore,
- TOKEN_ADDR: self.__ignore,
- TOKEN_IGNORE: self.__ignore,
- TOKEN_STAT: self.__ignore,
- TOKEN_TEMP: self.__ignore,
- TOKEN_OBJE: self.__ignore,
- TOKEN_SOUR: self.__person_attr_source,
- TOKEN_PLAC: self.__person_attr_place,
- TOKEN_NOTE: self.__person_attr_note,
- TOKEN_RNOTE: self.__person_attr_note,
- }
- self.func_list.append(self.person_attr_parse_tbl)
-
- self.lds_parse_tbl = {
- TOKEN_TEMP: self.__lds_temple,
- TOKEN_DATE: self.__lds_date,
- TOKEN_FAMC: self.__lds_famc,
- TOKEN_FORM: self.__lds_form,
- TOKEN_PLAC: self.__lds_plac,
- TOKEN_SOUR: self.__lds_sour,
- TOKEN_NOTE: self.__lds_note,
- TOKEN_RNOTE: self.__lds_note,
- TOKEN_STAT: self.__lds_stat,
- }
- self.func_list.append(self.lds_parse_tbl)
-
- self.asso_parse_tbl = {
- TOKEN_RELA: self.__person_asso_rela,
- TOKEN_SOUR: self.__person_asso_sour,
- TOKEN_NOTE: self.__person_asso_note,
- TOKEN_RNOTE: self.__person_asso_note,
- }
- self.func_list.append(self.asso_parse_tbl)
-
- self.citation_parse_tbl = {
- TOKEN_PAGE: self.__citation_page,
- TOKEN_DATE: self.__citation_date,
- TOKEN_DATA: self.__citation_data,
- TOKEN_OBJE: self.__citation_obje,
- TOKEN_REFN: self.__citation_refn,
- TOKEN_EVEN: self.__citation_even,
- TOKEN_IGNORE: self.__ignore,
- TOKEN__LKD: self.__ignore,
- TOKEN_QUAY: self.__citation_quay,
- TOKEN_NOTE: self.__citation_note,
- TOKEN_RNOTE: self.__citation_note,
- TOKEN_TEXT: self.__citation_data_text,
- TOKEN__LINK: self.__citation_link,
- TOKEN__JUST: self.__citation__just,
- TOKEN__APID: self.__citation__apid,
- }
- self.func_list.append(self.citation_parse_tbl)
-
- self.media_parse_tbl = {
- TOKEN_FORM: self.__media_ref_form,
- TOKEN_MEDI: self.__media_ref_medi, # v5.5.1
- TOKEN_TITL: self.__media_ref_titl,
- TOKEN_FILE: self.__media_ref_file,
- TOKEN_NOTE: self.__obje_note, # illegal, but often there
- TOKEN_RNOTE: self.__obje_note, # illegal, but often there
- TOKEN__PRIM: self.__media_ref_prim, # LFT etc.
- TOKEN_IGNORE: self.__ignore,
- }
- self.func_list.append(self.media_parse_tbl)
-
- self.parse_loc_tbl = {
- TOKEN_ADR1: self.__location_adr1,
- TOKEN_ADR2: self.__location_adr2,
- TOKEN_CITY: self.__location_city,
- TOKEN_STAE: self.__location_stae,
- TOKEN_POST: self.__location_post,
- TOKEN_CTRY: self.__location_ctry,
- # Not legal GEDCOM - not clear why these are included at this level
- TOKEN_ADDR: self.__ignore,
- TOKEN_DATE: self.__ignore, # there is nowhere to put a date
- TOKEN_NOTE: self.__location_note,
- TOKEN_RNOTE: self.__location_note,
- TOKEN__LOC: self.__ignore,
- TOKEN__NAME: self.__ignore,
- TOKEN_PHON: self.__location_phone,
- TOKEN_IGNORE: self.__ignore,
- }
- self.func_list.append(self.parse_loc_tbl)
-
- #
- # Parse table for <> below the level 0 FAM tag
- #
- # n @@ FAM {1:1}
- # +1 <> {0:M}
- # +1 HUSB @@ {0:1}
- # +1 WIFE @@ {0:1}
- # +1 CHIL @@ {0:M}
- # +1 NCHI {0:1}
- # +1 SUBM @@ {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 REFN {0:M}
- # +1 RIN {0:1}
- # +1 <> {0:1}
-
- self.family_func = {
- # +1 <> {0:M}
- TOKEN_GEVENT: self.__family_std_event,
- TOKEN_EVEN: self.__fam_even,
- # +1 HUSB @@ {0:1}
- TOKEN_HUSB: self.__family_husb,
- # +1 WIFE @@ {0:1}
- TOKEN_WIFE: self.__family_wife,
- # +1 CHIL @@ {0:M}
- TOKEN_CHIL: self.__family_chil,
- # +1 NCHI {0:1}
- # +1 SUBM @@ {0:M}
- # +1 <> {0:M}
- TOKEN_SLGS: self.__family_slgs,
- # +1 <> {0:M}
- TOKEN_SOUR: self.__family_source,
- # +1 <> {0:M}
- TOKEN_OBJE: self.__family_object,
- # +1 <> {0:M}
- TOKEN__COMM: self.__family_comm,
- TOKEN_NOTE: self.__family_note,
- TOKEN_RNOTE: self.__family_note,
- # +1 REFN {0:M}
- TOKEN_REFN: self.__family_refn,
- # TYPE should be below REFN, but will work here anyway
- TOKEN_TYPE: self.__family_cust_attr,
- # +1 RIN {0:1}
- # +1 <> {0:1}
- TOKEN_CHAN: self.__family_chan,
- TOKEN_ENDL: self.__ignore,
- TOKEN_ADDR: self.__ignore,
- TOKEN_RIN: self.__family_cust_attr,
- TOKEN_SUBM: self.__ignore,
- TOKEN_ATTR: self.__family_attr,
- }
- self.func_list.append(self.family_func)
-
- self.family_rel_tbl = {
- TOKEN__FREL: self.__family_frel,
- TOKEN__MREL: self.__family_mrel,
- TOKEN_ADOP: self.__family_adopt,
- TOKEN__STAT: self.__family_stat,
- }
- self.func_list.append(self.family_rel_tbl)
-
- #
- # Parse table for <> below the level 0 SOUR tag
- #
- # n @@ SOUR {1:1}
- # +1 DATA {0:1}
- # +2 EVEN {0:M}
- # +3 DATE {0:1}
- # +3 PLAC {0:1}
- # +2 AGNC {0:1}
- # +2 <> {0:M}
- # +1 AUTH {0:1}
- # +2 [CONT|CONC] {0:M}
- # +1 TITL {0:1}
- # +2 [CONT|CONC] {0:M}
- # +1 ABBR {0:1}
- # +1 PUBL {0:1}
- # +2 [CONT|CONC] {0:M}
- # +1 TEXT {0:1}
- # +2 [CONT|CONC] {0:M}
- # +1 <> {0:1}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:1}
-
- self.source_func = {
- TOKEN_TITL: self.__source_title,
- TOKEN_TAXT: self.__source_taxt_peri,
- TOKEN_PERI: self.__source_taxt_peri,
- TOKEN_AUTH: self.__source_auth,
- TOKEN_PUBL: self.__source_publ,
- TOKEN_NOTE: self.__source_note,
- TOKEN_RNOTE: self.__source_note,
- TOKEN_TEXT: self.__source_text,
- TOKEN_ABBR: self.__source_abbr,
- TOKEN_REFN: self.__source_attr,
- TOKEN_RIN: self.__source_attr,
- TOKEN_REPO: self.__source_repo,
- TOKEN_OBJE: self.__source_object,
- TOKEN_CHAN: self.__source_chan,
- TOKEN_MEDI: self.__source_attr,
- TOKEN__NAME: self.__source_attr,
- TOKEN_DATA: self.__ignore,
- # TYPE should be below REFN, but will work here anyway
- TOKEN_TYPE: self.__source_attr,
- TOKEN_CALN: self.__ignore,
- # not legal, but Ultimate Family Tree does this
- TOKEN_DATE: self.__ignore,
- TOKEN_IGNORE: self.__ignore,
- TOKEN__APID: self.__source_attr,
- }
- self.func_list.append(self.source_func)
-
- #
- # Parse table for <> below the level 0 OBJE tag
- #
- # n @XREF:OBJE@ OBJE {1:1} # v5.5 layout
- # +1 FILE {1:1} # de-facto extension
- # +1 FORM {1:1}
- # +1 TITL {0:1}
- # +1 <> {0:M}
- # +1 BLOB {1:1} # Deprecated, no support
- # +2 CONT {1:M}
- # +1 OBJE @@ /* chain */ {0:1} # Deprecated, no support
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:1}
- #
- # n @XREF:OBJE@ OBJE {1:1} # v5.5.1 layout
- # +1 FILE {1:M} # multi files, no support
- # +2 FORM {1:1}
- # +3 TYPE {0:1}
- # +2 TITL {0:1}
- # +2 DATE {0:1} # FTM extension
- # +2 TEXT {0:1} # FTM extension
- # +1 REFN {0:M}
- # +2 TYPE {0:1}
- # +1 RIN {0:1}
- # +1 <> {0:M}
- # +1 <> {0:M}
- # +1 <> {0:1}
-
- self.obje_func = {
- TOKEN_FORM: self.__obje_form,
- TOKEN_TYPE: self.__obje_type, # v5.5.1
- TOKEN_TITL: self.__obje_title,
- TOKEN_FILE: self.__obje_file, # de-facto extension
- TOKEN_TEXT: self.__obje_text, # FTM extension
- TOKEN__TEXT: self.__obje_text, # FTM 2017 extension
- TOKEN_DATE: self.__obje_date, # FTM extension
- TOKEN__DATE: self.__obje_date, # FTM 2017 extension
- TOKEN_NOTE: self.__obje_note,
- TOKEN_RNOTE: self.__obje_note,
- TOKEN_SOUR: self.__obje_sour,
- TOKEN_BLOB: self.__ignore, # v5.5.1 deprecated
- TOKEN_REFN: self.__obje_refn,
- TOKEN_RIN: self.__obje_rin,
- TOKEN_CHAN: self.__obje_chan,
- }
- self.func_list.append(self.obje_func)
-
- self.parse_addr_tbl = {
- TOKEN_DATE: self.__address_date,
- TOKEN_ADR1: self.__address_adr1,
- TOKEN_ADR2: self.__address_adr2,
- TOKEN_CITY: self.__address_city,
- TOKEN_STAE: self.__address_state,
- TOKEN_POST: self.__address_post,
- TOKEN_CTRY: self.__address_country,
- TOKEN_PHON: self.__ignore,
- TOKEN_SOUR: self.__address_sour,
- TOKEN_NOTE: self.__address_note,
- TOKEN_RNOTE: self.__address_note,
- TOKEN__LOC: self.__ignore,
- TOKEN__NAME: self.__ignore,
- TOKEN_IGNORE: self.__ignore,
- TOKEN_TYPE: self.__ignore,
- TOKEN_CAUS: self.__ignore,
- }
- self.func_list.append(self.parse_addr_tbl)
-
- self.event_cause_tbl = {
- TOKEN_SOUR: self.__event_cause_source,
- }
- self.func_list.append(self.event_cause_tbl)
-
- self.event_place_map = {
- TOKEN_NOTE: self.__event_place_note,
- TOKEN_RNOTE: self.__event_place_note,
- TOKEN_FORM: self.__event_place_form,
- # Not legal.
- TOKEN_OBJE: self.__event_place_object,
- TOKEN_SOUR: self.__event_place_sour,
- TOKEN__LOC: self.__ignore,
- TOKEN_MAP: self.__place_map,
- # Not legal, but generated by Ultimate Family Tree
- TOKEN_QUAY: self.__ignore,
- }
- self.func_list.append(self.event_place_map)
-
- self.place_map_tbl = {
- TOKEN_LATI: self.__place_lati,
- TOKEN_LONG: self.__place_long,
- }
- self.func_list.append(self.place_map_tbl)
-
- self.repo_ref_tbl = {
- TOKEN_CALN: self.__repo_ref_call,
- TOKEN_NOTE: self.__repo_ref_note,
- TOKEN_RNOTE: self.__repo_ref_note,
- TOKEN_MEDI: self.__repo_ref_medi,
- TOKEN_IGNORE: self.__ignore,
- }
- self.func_list.append(self.repo_ref_tbl)
-
- self.parse_person_adopt = {
- TOKEN_ADOP: self.__person_adopt_famc_adopt,
- }
- self.func_list.append(self.parse_person_adopt)
-
- self.opt_note_tbl = {
- TOKEN_RNOTE: self.__optional_note,
- TOKEN_NOTE: self.__optional_note,
- }
- self.func_list.append(self.opt_note_tbl)
-
- self.citation_data_tbl = {
- TOKEN_DATE: self.__citation_data_date,
- TOKEN_TEXT: self.__citation_data_text,
- TOKEN_RNOTE: self.__citation_data_note,
- TOKEN_NOTE: self.__citation_data_note,
- }
- self.func_list.append(self.citation_data_tbl)
-
- self.citation_even_tbl = {
- TOKEN_ROLE: self.__citation_even_role,
- }
- self.func_list.append(self.citation_even_tbl)
-
- #
- # Parse table for <> record below the level 0 HEAD tag
- #
- # n HEAD {1:1}
- # +1 SOUR {1:1}
- # +2 VERS {0:1}
- # +2 NAME {0:1}
- # +2 CORP {0:1}
- # +3 <> {0:1}
- # +2 DATA {0:1}
- # +3 DATE {0:1}
- # +3 COPR {0:1}
- # +1 DEST {0:1*}
- # +1 DATE {0:1}
- # +2 TIME {0:1}
- # +1 SUBM @@ {1:1}
- # +1 SUBN @@ {0:1}
- # +1 FILE {0:1}
- # +1 COPR {0:1}
- # +1 GEDC {1:1}
- # +2 VERS {1:1}
- # +2 FORM {1:1}
- # +1 CHAR {1:1}
- # +2 VERS {0:1}
- # +1 LANG {0:1}
- # +1 PLAC {0:1}
- # +2 FORM {1:1}
- # +1 NOTE {0:1}
- # +2 [CONT|CONC] {0:M}
-
- # * NOTE: Submissions to the Family History Department for Ancestral
- # File submission or for clearing temple ordinances must use a
- # DESTination of ANSTFILE or TempleReady.
-
- self.head_parse_tbl = {
- TOKEN_SOUR: self.__header_sour,
- TOKEN_NAME: self.__header_sour_name, # This should be below SOUR
- TOKEN_VERS: self.__header_sour_vers, # This should be below SOUR
- TOKEN_FILE: self.__header_file,
- TOKEN_COPR: self.__header_copr,
- TOKEN_SUBM: self.__header_subm,
- TOKEN_CORP: self.__ignore, # This should be below SOUR
- TOKEN_DATA: self.__ignore, # This should be below SOUR
- TOKEN_SUBN: self.__header_subn,
- TOKEN_LANG: self.__header_lang,
- TOKEN_TIME: self.__ignore, # This should be below DATE
- TOKEN_DEST: self.__header_dest,
- TOKEN_CHAR: self.__header_char,
- TOKEN_GEDC: self.__header_gedc,
- TOKEN_PLAC: self.__header_plac,
- TOKEN_DATE: self.__header_date,
- TOKEN_NOTE: self.__header_note,
- TOKEN__SCHEMA: self.__ignore,
- }
- self.func_list.append(self.head_parse_tbl)
-
- self.header_sour_parse_tbl = {
- TOKEN_VERS: self.__header_sour_vers,
- TOKEN_NAME: self.__header_sour_name,
- TOKEN_CORP: self.__header_sour_corp,
- TOKEN_DATA: self.__header_sour_data,
- }
- self.func_list.append(self.header_sour_parse_tbl)
-
- self.header_sour_data = {
- TOKEN_DATE: self.__header_sour_date,
- TOKEN_COPR: self.__header_sour_copr,
- }
- self.func_list.append(self.header_sour_data)
-
- self.header_corp_addr = {
- TOKEN_ADDR: self.__repo_addr,
- TOKEN_PHON: self.__repo_phon,
- TOKEN_FAX: self.__repo_fax,
- TOKEN_WWW: self.__repo_www,
- TOKEN_EMAIL: self.__repo_email,
- }
- self.func_list.append(self.header_corp_addr)
-
- self.header_subm = {
- TOKEN_NAME: self.__header_subm_name,
- }
- self.func_list.append(self.header_subm)
-
- self.place_form = {
- TOKEN_FORM: self.__place_form,
- }
- self.func_list.append(self.place_form)
-
- #
- # Parse table for <> below the level 0 NOTE tag
- #
- # n @@ NOTE {1:1}
- # +1 [ CONC | CONT] {0:M}
- # +1 <> {0:M}
- # +1 REFN