From 9c4f7b7e0a32f24280f26c058d4e303bfa74249d Mon Sep 17 00:00:00 2001 From: Tim G L Lyons Date: Tue, 13 Mar 2012 17:09:08 +0000 Subject: [PATCH] 0005620: Export name types to GEDCOM. Patch extended following testing against various GEDCOM files from the internet. svn: r19053 --- src/plugins/export/ExportGedcom.py | 8 ++ src/plugins/lib/libgedcom.py | 200 ++++++++++++++++++++++++----- 2 files changed, 177 insertions(+), 31 deletions(-) diff --git a/src/plugins/export/ExportGedcom.py b/src/plugins/export/ExportGedcom.py index e7b1f6d90..cfd7c6879 100644 --- a/src/plugins/export/ExportGedcom.py +++ b/src/plugins/export/ExportGedcom.py @@ -1257,6 +1257,14 @@ class GedcomWriter(UpdateCallback): nick = attr_nick self.__writeln(1, 'NAME', gedcom_name) + if int(name.get_type()) == gen.lib.NameType.BIRTH: + pass + elif int(name.get_type()) == gen.lib.NameType.MARRIED: + self.__writeln(2, 'TYPE', 'married') + elif int(name.get_type()) == gen.lib.NameType.AKA: + self.__writeln(2, 'TYPE', 'aka') + else: + self.__writeln(2, 'TYPE', name.get_type().xml_str()) if firstname: self.__writeln(2, 'GIVN', firstname) diff --git a/src/plugins/lib/libgedcom.py b/src/plugins/lib/libgedcom.py index acc412023..b775d176b 100644 --- a/src/plugins/lib/libgedcom.py +++ b/src/plugins/lib/libgedcom.py @@ -255,6 +255,8 @@ TOKEN_WWW = 125 TOKEN_URL = 126 TOKEN_ROLE = 127 TOKEN__MAR = 128 +TOKEN__MARN = 129 +TOKEN__ADPN = 130 TOKENS = { "HEAD" : TOKEN_HEAD, "MEDI" : TOKEN_MEDI, @@ -268,6 +270,7 @@ TOKENS = { "ADDRESS2" : TOKEN_ADR2, "AFN" : TOKEN_AFN, "AGE" : TOKEN_AGE, "AGNC" : TOKEN_AGNC, "AGENCY" : TOKEN_IGNORE, "_AKA" : TOKEN__AKA, + "_AKAN" : TOKEN__AKA, "AKA" : TOKEN__AKA, "_ALIA" : TOKEN_ALIA, "ALIA" : TOKEN_ALIA, "ALIAS" : TOKEN_ALIA, "ANCI" : TOKEN_ANCI, "ASSO" : TOKEN_ASSO, "ASSOCIATES" : TOKEN_ASSO, @@ -358,7 +361,8 @@ TOKENS = { "FACT" : TOKEN_FACT, "EMAIL" : TOKEN_EMAIL, "EMAI" : TOKEN_EMAIL, "WWW" : TOKEN_WWW, "_URL" : TOKEN_URL, "URL" : TOKEN_URL, - "_MAR" : TOKEN__MAR, + "_MAR" : TOKEN__MAR, "_MARN" : TOKEN__MARN, + "_ADPN" : TOKEN__ADPN } ADOPT_NONE = 0 @@ -1928,7 +1932,7 @@ class GedcomParser(UpdateCallback): # +1 REFN {0:M} # +2 TYPE {0:1} TOKEN_REFN : self.__person_attr, - # TYPE should be eblow REFN, but will work here anyway + # TYPE should be below REFN, but will work here anyway TOKEN_TYPE : self.__person_attr, # +1 RIN {0:1} TOKEN_RIN : self.__person_attr, @@ -1964,10 +1968,18 @@ class GedcomParser(UpdateCallback): # Extensions TOKEN_ALIA : self.__name_alia, TOKEN__MARNM : self.__name_marnm, - TOKEN__MAR : self.__name_marnm, # Generated by gni.com - TOKEN__AKA : self.__name_aka, - TOKEN_TYPE : self.__name_type, + 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, } # @@ -3143,8 +3155,13 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ + # We can get here when a tag that is not valid in the indi_parse_tbl + # parse table is encountered. It is remotely possible that this is + # actally a DATE tag, in which case line.data will be a date object, so + # we need to convert it back to a string here. event_ref = self.__build_event_pair(state, gen.lib.EventType.CUSTOM, - self.event_parse_tbl, line.data) + self.event_parse_tbl, + str(line.data)) state.person.add_event_ref(event_ref) def __fam_even(self, line, state): @@ -3184,14 +3201,41 @@ class GedcomParser(UpdateCallback): def __person_alt_name(self, line, state): """ + This parses the standard GEDCOM structure: + + n @XREF:INDI@ INDI {1:1} + +1 ALIA @@ {0:M} + The ALIA tag is supposed to cross reference another person. We will store this in the Association record. ALIA {ALIAS}: = An indicator to link different record descriptions of a person who may be the same person. - - Some systems use the ALIA tag as an alternate NAME tag, which - is not legal in GEDCOM, but oddly enough, is easy to support. + + Some systems use the ALIA tag as an alternate NAME tag, which is not + legal in GEDCOM, but oddly enough, is easy to support. This parses the + illegal (ALIA or ALIAS) or non-standard (_ALIA) GEDCOM. "1 ALIA" is used + by Family Tree Maker and Reunion. "1 ALIAS" and "1 _ALIA" do not appear + to be used. + + n @XREF:INDI@ INDI {1:1} + +1 {1:1} + +2 NPFX {0:1} + +2 GIVN {0:1} + +2 NICK {0:1} + +2 SPFX {0:1} + +2 SURN {0:1} + +2 NSFX {0:1} + +2 <> {0:M} + +3 <> {0:M} + +3 <> {0:M} + +2 <> {0:M} + where == ALIA | _ALIA | ALIAS + + @param line: The current line in GedLine format + @type line: GedLine + @param state: The current state + @type state: CurrentState """ if line.data[0] == '@': handle = self.__find_person_handle(self.pid_map[line.data]) @@ -3204,11 +3248,23 @@ class GedcomParser(UpdateCallback): def __parse_alias_name(self, line, state): """ - Parse a altername name, usually indicated by a AKA or _AKA - tag. This is not valid GEDCOM, but several programs will add - this just to make life interesting. Odd, since GEDCOM supports - multiple NAME indicators, which is the correct way of handling - multiple names. + Parse a level 1 alias name and subsidiary levels when called from + __person_alt_name (when the does not start with @). Also + parses a level 2 alias name and subsidiary levels when called from + __name_alias. + + +1 {1:1} + +2 NPFX {0:1} + +2 GIVN {0:1} + +2 NICK {0:1} + +2 SPFX {0:1} + +2 SURN {0:1} + +2 NSFX {0:1} + +2 <> {0:M} + +3 <> {0:M} + +3 <> {0:M} + +2 <> {0:M} + where == ALIA | _ALIA | ALIAS @param line: The current line in GedLine format @type line: GedLine @@ -3595,11 +3651,23 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - if line.data == "_OTHN": + if line.data.upper() in ("_OTHN", "_AKA", "AKA", "AKAN"): state.name.set_type(gen.lib.NameType.AKA) + elif line.data.upper() in ("_MAR", "_MARN", "_MARNM", "MARRIED"): + state.name.set_type(gen.lib.NameType.MARRIED) else: state.name.set_type((gen.lib.NameType.CUSTOM, line.data)) + def __name_date(self, line, state): + """ + @param line: The current line in GedLine format + @type line: GedLine + @param state: The current state + @type state: CurrentState + """ + if state.name: + state.name.set_date_object(line.data) + def __name_note(self, line, state): """ @param line: The current line in GedLine format @@ -3611,27 +3679,46 @@ class GedcomParser(UpdateCallback): def __name_alia(self, line, state): """ - This handles the ALIA (or _ALIA or ALIAS) tag as a subsidiary of the - NAME tag. For example: + This parses the illegal (ALIA or ALIAS) or non-standard (_ALIA) GEDCOM + tag as a subsidiary of the NAME tag. - 0 @PERSON1@ INDI - 1 NAME John /Doe/ - 2 GIVN John - 2 SURN Doe - 2 ALIA Richard /Roe/ - 2 NPFX Dr. - 2 GIVN Richard - 2 NICK Rich - 2 SPFX Le - + n @XREF:INDI@ INDI {1:1} + +1 NAME {1:1} + +2 NPFX {0:1} + +2 GIVN {0:1} + +2 NICK {0:1} + +2 SPFX {0:1} + +2 SURN {0:1} + +2 NSFX {0:1} + +2 {1:1} + +3 NPFX {0:1} + +3 GIVN {0:1} + +3 NICK {0:1} + +3 SPFX {0:1} + +3 SURN {0:1} + +3 NSFX {0:1} + +3 <> {0:M} + +4 <> {0:M} + +4 <> {0:M} + +3 <> {0:M} + +2 <> {0:M} + +3 <> {0:M} + +3 <> {0:M} + +2 <> {0:M} + Note that the subsidiary name structure detail will overwrite the ALIA name (if the same elements are provided in both), so the names should match. - There does not appear to be any evidence that this usage exists, but as - it was supported (though probably incorrectly coded as it would only - work if the name started with'@'), in previous versions of Gramps, we - had better support it here. + "2 _ALIA" is used for example, by PRO-GEN v 3.0a and "2 ALIA" is used + by GTEdit and Brother's keeper 5.2 for windows. It had been supported in + previous versions of Gramps but as it was probably incorrectly coded as + it would only work if the name started with '@'. + + @param line: The current line in GedLine format + @type line: GedLine + @param state: The current state + @type state: CurrentState """ self.__parse_alias_name(line, state) @@ -3689,6 +3776,16 @@ class GedcomParser(UpdateCallback): def __name_marnm(self, line, state): """ + This is non-standard GEDCOM. _MARNM is reported to be used in Ancestral + Quest and Personal Ancestral File 5. This will also handle a usage which + has been found in Brother's Keeper (BROSKEEP VERS 6.1.31 WINDOWS) as + follows: + + 0 @I203@ INDI + 1 NAME John Richard/Doe/ + 2 _MARN Some Other Name + 3 DATE 27 JUN 1817 + @param line: The current line in GedLine format @type line: GedLine @param state: The current state @@ -3737,6 +3834,22 @@ class GedcomParser(UpdateCallback): def __name_aka(self, line, state): """ + This parses the non-standard GEDCOM tags _AKA or _AKAN as a subsidiary + to the NAME tag, which is reported to have been found in Ancestral Quest + and Personal Ancestral File 4 and 5. Note: example AQ and PAF files have + separate 2 NICK and 2 _AKA lines for the same person. The NICK will be + stored by Gramps in the nick_name field of the name structure, while the + _AKA, if it is a single word, will be stored in the NICKNAME attribute. + If more than one word it is stored as an AKA alternate name. + + This will also handle a usage which has been found in in Brother's + Keeper (BROSKEEP VERS 6.1.31 WINDOWS) as follows: + + 0 @I203@ INDI + 1 NAME John Richard/Doe/ + 2 _AKAN Some Other Name + 3 DATE 27 JUN 1817 + @param line: The current line in GedLine format @type line: GedLine @param state: The current state @@ -3756,8 +3869,33 @@ class GedcomParser(UpdateCallback): surname.set_primary() name.set_surname_list([surname]) name.set_first_name(' '.join(lname[0:name_len-1])) +# name = self.__parse_name_personal(line.data) + name.set_type(gen.lib.NameType.AKA) state.person.add_alternate_name(name) + def __name_adpn(self, line, state): + """ + @param line: The current line in GedLine format + @type line: GedLine + @param state: The current state + @type state: CurrentState + """ + text = line.data.strip() + data = text.split() + if len(data) == 1: + name = gen.lib.Name(state.person.primary_name) + surn = gen.lib.Surname() + surn.set_surname(data[0].strip()) + surn.set_primary() + name.set_surname_list([surn]) + name.set_type((gen.lib.NameType.CUSTOM, "Adopted")) + state.person.add_alternate_name(name) + elif len(data) > 1: + name = self.__parse_name_personal(text) + name.set_type((gen.lib.NameType.CUSTOM, "Adopted")) + state.person.add_alternate_name(name) + + def __name_sour(self, line, state): """ @param line: The current line in GedLine format