0005620: Export name types to GEDCOM. Patch extended following testing against various GEDCOM files from the internet.

svn: r19054
This commit is contained in:
Tim G L Lyons 2012-03-13 17:12:23 +00:00
parent 44667f7f13
commit 73be67b3f8
2 changed files with 177 additions and 31 deletions

View File

@ -1257,6 +1257,14 @@ class GedcomWriter(UpdateCallback):
nick = attr_nick nick = attr_nick
self.__writeln(1, 'NAME', gedcom_name) 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: if firstname:
self.__writeln(2, 'GIVN', firstname) self.__writeln(2, 'GIVN', firstname)

View File

@ -255,6 +255,8 @@ TOKEN_WWW = 125
TOKEN_URL = 126 TOKEN_URL = 126
TOKEN_ROLE = 127 TOKEN_ROLE = 127
TOKEN__MAR = 128 TOKEN__MAR = 128
TOKEN__MARN = 129
TOKEN__ADPN = 130
TOKENS = { TOKENS = {
"HEAD" : TOKEN_HEAD, "MEDI" : TOKEN_MEDI, "HEAD" : TOKEN_HEAD, "MEDI" : TOKEN_MEDI,
@ -268,6 +270,7 @@ TOKENS = {
"ADDRESS2" : TOKEN_ADR2, "AFN" : TOKEN_AFN, "ADDRESS2" : TOKEN_ADR2, "AFN" : TOKEN_AFN,
"AGE" : TOKEN_AGE, "AGNC" : TOKEN_AGNC, "AGE" : TOKEN_AGE, "AGNC" : TOKEN_AGNC,
"AGENCY" : TOKEN_IGNORE, "_AKA" : TOKEN__AKA, "AGENCY" : TOKEN_IGNORE, "_AKA" : TOKEN__AKA,
"_AKAN" : TOKEN__AKA, "AKA" : TOKEN__AKA,
"_ALIA" : TOKEN_ALIA, "ALIA" : TOKEN_ALIA, "_ALIA" : TOKEN_ALIA, "ALIA" : TOKEN_ALIA,
"ALIAS" : TOKEN_ALIA, "ANCI" : TOKEN_ANCI, "ALIAS" : TOKEN_ALIA, "ANCI" : TOKEN_ANCI,
"ASSO" : TOKEN_ASSO, "ASSOCIATES" : TOKEN_ASSO, "ASSO" : TOKEN_ASSO, "ASSOCIATES" : TOKEN_ASSO,
@ -358,7 +361,8 @@ TOKENS = {
"FACT" : TOKEN_FACT, "EMAIL" : TOKEN_EMAIL, "FACT" : TOKEN_FACT, "EMAIL" : TOKEN_EMAIL,
"EMAI" : TOKEN_EMAIL, "WWW" : TOKEN_WWW, "EMAI" : TOKEN_EMAIL, "WWW" : TOKEN_WWW,
"_URL" : TOKEN_URL, "URL" : TOKEN_URL, "_URL" : TOKEN_URL, "URL" : TOKEN_URL,
"_MAR" : TOKEN__MAR, "_MAR" : TOKEN__MAR, "_MARN" : TOKEN__MARN,
"_ADPN" : TOKEN__ADPN
} }
ADOPT_NONE = 0 ADOPT_NONE = 0
@ -1928,7 +1932,7 @@ class GedcomParser(UpdateCallback):
# +1 REFN <USER_REFERENCE_NUMBER> {0:M} # +1 REFN <USER_REFERENCE_NUMBER> {0:M}
# +2 TYPE <USER_REFERENCE_TYPE> {0:1} # +2 TYPE <USER_REFERENCE_TYPE> {0:1}
TOKEN_REFN : self.__person_attr, 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, TOKEN_TYPE : self.__person_attr,
# +1 RIN <AUTOMATED_RECORD_ID> {0:1} # +1 RIN <AUTOMATED_RECORD_ID> {0:1}
TOKEN_RIN : self.__person_attr, TOKEN_RIN : self.__person_attr,
@ -1964,10 +1968,18 @@ class GedcomParser(UpdateCallback):
# Extensions # Extensions
TOKEN_ALIA : self.__name_alia, TOKEN_ALIA : self.__name_alia,
TOKEN__MARNM : self.__name_marnm, TOKEN__MARNM : self.__name_marnm,
TOKEN__MAR : self.__name_marnm, # Generated by gni.com TOKEN__MAR : self.__name_marnm, # Generated by geni.com
TOKEN__AKA : self.__name_aka, TOKEN__MARN : self.__name_marnm, # Gen'd by BROSKEEP 6.1.31 WIN
TOKEN_TYPE : self.__name_type, 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_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 @param state: The current state
@type state: CurrentState @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, 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) state.person.add_event_ref(event_ref)
def __fam_even(self, line, state): def __fam_even(self, line, state):
@ -3184,14 +3201,41 @@ class GedcomParser(UpdateCallback):
def __person_alt_name(self, line, state): def __person_alt_name(self, line, state):
""" """
This parses the standard GEDCOM structure:
n @XREF:INDI@ INDI {1:1}
+1 ALIA @<XREF:INDI>@ {0:M}
The ALIA tag is supposed to cross reference another person. We will The ALIA tag is supposed to cross reference another person. We will
store this in the Association record. store this in the Association record.
ALIA {ALIAS}: = An indicator to link different record descriptions of a ALIA {ALIAS}: = An indicator to link different record descriptions of a
person who may be the same person. person who may be the same person.
Some systems use the ALIA tag as an alternate NAME tag, which Some systems use the ALIA tag as an alternate NAME tag, which is not
is not legal in GEDCOM, but oddly enough, is easy to support. 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 <ALIA> <NAME_PERSONAL> {1:1}
+2 NPFX <NAME_PIECE_PREFIX> {0:1}
+2 GIVN <NAME_PIECE_GIVEN> {0:1}
+2 NICK <NAME_PIECE_NICKNAME> {0:1}
+2 SPFX <NAME_PIECE_SURNAME_PREFIX> {0:1}
+2 SURN <NAME_PIECE_SURNAME> {0:1}
+2 NSFX <NAME_PIECE_SUFFIX> {0:1}
+2 <<SOURCE_CITATION>> {0:M}
+3 <<NOTE_STRUCTURE>> {0:M}
+3 <<MULTIMEDIA_LINK>> {0:M}
+2 <<NOTE_STRUCTURE>> {0:M}
where <ALIA> == 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] == '@': if line.data[0] == '@':
handle = self.__find_person_handle(self.pid_map[line.data]) handle = self.__find_person_handle(self.pid_map[line.data])
@ -3204,11 +3248,23 @@ class GedcomParser(UpdateCallback):
def __parse_alias_name(self, line, state): def __parse_alias_name(self, line, state):
""" """
Parse a altername name, usually indicated by a AKA or _AKA Parse a level 1 alias name and subsidiary levels when called from
tag. This is not valid GEDCOM, but several programs will add __person_alt_name (when the <NAME_PERSONAL> does not start with @). Also
this just to make life interesting. Odd, since GEDCOM supports parses a level 2 alias name and subsidiary levels when called from
multiple NAME indicators, which is the correct way of handling __name_alias.
multiple names.
+1 <ALIA> <NAME_PERSONAL> {1:1}
+2 NPFX <NAME_PIECE_PREFIX> {0:1}
+2 GIVN <NAME_PIECE_GIVEN> {0:1}
+2 NICK <NAME_PIECE_NICKNAME> {0:1}
+2 SPFX <NAME_PIECE_SURNAME_PREFIX> {0:1}
+2 SURN <NAME_PIECE_SURNAME> {0:1}
+2 NSFX <NAME_PIECE_SUFFIX> {0:1}
+2 <<SOURCE_CITATION>> {0:M}
+3 <<NOTE_STRUCTURE>> {0:M}
+3 <<MULTIMEDIA_LINK>> {0:M}
+2 <<NOTE_STRUCTURE>> {0:M}
where <ALIA> == ALIA | _ALIA | ALIAS
@param line: The current line in GedLine format @param line: The current line in GedLine format
@type line: GedLine @type line: GedLine
@ -3595,11 +3651,23 @@ class GedcomParser(UpdateCallback):
@param state: The current state @param state: The current state
@type state: CurrentState @type state: CurrentState
""" """
if line.data == "_OTHN": if line.data.upper() in ("_OTHN", "_AKA", "AKA", "AKAN"):
state.name.set_type(gen.lib.NameType.AKA) 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: else:
state.name.set_type((gen.lib.NameType.CUSTOM, line.data)) 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): def __name_note(self, line, state):
""" """
@param line: The current line in GedLine format @param line: The current line in GedLine format
@ -3611,27 +3679,46 @@ class GedcomParser(UpdateCallback):
def __name_alia(self, line, state): def __name_alia(self, line, state):
""" """
This handles the ALIA (or _ALIA or ALIAS) tag as a subsidiary of the This parses the illegal (ALIA or ALIAS) or non-standard (_ALIA) GEDCOM
NAME tag. For example: tag as a subsidiary of the NAME tag.
0 @PERSON1@ INDI n @XREF:INDI@ INDI {1:1}
1 NAME John /Doe/ +1 NAME <NAME_PERSONAL> {1:1}
2 GIVN John +2 NPFX <NAME_PIECE_PREFIX> {0:1}
2 SURN Doe +2 GIVN <NAME_PIECE_GIVEN> {0:1}
2 ALIA Richard /Roe/ +2 NICK <NAME_PIECE_NICKNAME> {0:1}
2 NPFX Dr. +2 SPFX <NAME_PIECE_SURNAME_PREFIX> {0:1}
2 GIVN Richard +2 SURN <NAME_PIECE_SURNAME> {0:1}
2 NICK Rich +2 NSFX <NAME_PIECE_SUFFIX> {0:1}
2 SPFX Le +2 <ALIA> <NAME_PERSONAL> {1:1}
+3 NPFX <NAME_PIECE_PREFIX> {0:1}
+3 GIVN <NAME_PIECE_GIVEN> {0:1}
+3 NICK <NAME_PIECE_NICKNAME> {0:1}
+3 SPFX <NAME_PIECE_SURNAME_PREFIX> {0:1}
+3 SURN <NAME_PIECE_SURNAME> {0:1}
+3 NSFX <NAME_PIECE_SUFFIX> {0:1}
+3 <<SOURCE_CITATION>> {0:M}
+4 <<NOTE_STRUCTURE>> {0:M}
+4 <<MULTIMEDIA_LINK>> {0:M}
+3 <<NOTE_STRUCTURE>> {0:M}
+2 <<SOURCE_CITATION>> {0:M}
+3 <<NOTE_STRUCTURE>> {0:M}
+3 <<MULTIMEDIA_LINK>> {0:M}
+2 <<NOTE_STRUCTURE>> {0:M}
Note that the subsidiary name structure detail will overwrite the ALIA Note that the subsidiary name structure detail will overwrite the ALIA
name (if the same elements are provided in both), so the names should name (if the same elements are provided in both), so the names should
match. match.
There does not appear to be any evidence that this usage exists, but as "2 _ALIA" is used for example, by PRO-GEN v 3.0a and "2 ALIA" is used
it was supported (though probably incorrectly coded as it would only by GTEdit and Brother's keeper 5.2 for windows. It had been supported in
work if the name started with'@'), in previous versions of Gramps, we previous versions of Gramps but as it was probably incorrectly coded as
had better support it here. 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) self.__parse_alias_name(line, state)
@ -3689,6 +3776,16 @@ class GedcomParser(UpdateCallback):
def __name_marnm(self, line, state): 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 @param line: The current line in GedLine format
@type line: GedLine @type line: GedLine
@param state: The current state @param state: The current state
@ -3737,6 +3834,22 @@ class GedcomParser(UpdateCallback):
def __name_aka(self, line, state): 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 @param line: The current line in GedLine format
@type line: GedLine @type line: GedLine
@param state: The current state @param state: The current state
@ -3756,8 +3869,33 @@ class GedcomParser(UpdateCallback):
surname.set_primary() surname.set_primary()
name.set_surname_list([surname]) name.set_surname_list([surname])
name.set_first_name(' '.join(lname[0:name_len-1])) 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) 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): def __name_sour(self, line, state):
""" """
@param line: The current line in GedLine format @param line: The current line in GedLine format