From 846e472e09f006ce0e497f6162433d7dd9ab94a8 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Thu, 9 Sep 2010 16:52:09 +0000 Subject: [PATCH 01/28] create a branch for gep 021 work svn: r15865 From 86ed7108d290939ecca08167c436ca473aff390a Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Thu, 9 Sep 2010 16:54:34 +0000 Subject: [PATCH 02/28] surnamebase class to inherit surname lists svn: r15866 --- src/gen/lib/surnamebase.py | 156 +++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/gen/lib/surnamebase.py diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py new file mode 100644 index 000000000..a4786cb87 --- /dev/null +++ b/src/gen/lib/surnamebase.py @@ -0,0 +1,156 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2010 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id: addressbase.py 15645 2010-07-22 02:16:32Z dsblank $ + +""" +SurnameBase class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gen.lib.surname import Surname +from gen.lib.const import IDENTICAL, EQUAL + +#------------------------------------------------------------------------- +# +# SurnameBase classes +# +#------------------------------------------------------------------------- +class SurnameBase(object): + """ + Base class for surname-aware objects. + """ + + def __init__(self, source=None): + """ + Initialize a SurnameBase. + + If the source is not None, then object is initialized from values of + the source object. + + :param source: Object used to initialize the new object + :type source: SurnameBase + """ + self.surname_list = map(Surname, source.surname_list) if source else [] + + def serialize(self): + """ + Convert the object to a serialized tuple of data. + """ + return [surname.serialize() for surname in self.surname_list] + + def unserialize(self, data): + """ + Convert a serialized tuple of data to an object. + """ + self.surname_list = [Surname().unserialize(item) for item in data] + + def add_surname(self, surname): + """ + Add the :class:`~gen.lib.surname.Surname` instance to the object's + list of surnames. + + :param surname: :class:`~gen.lib.surname.Surname` instance to add to + the object's address list. + :type address: list + """ + self.surname_list.append(surname) + + def remove_surname(self, surname): + """ + Remove the specified :class:`~gen.lib.surname.Surname` instance from + the surname list. + + If the instance does not exist in the list, the operation has + no effect. + + :param surname: :class:`~gen.lib.surname.Surname` instance to remove + from the list + :type surname: :class:`~gen.lib.surname.Surname` + + :returns: True if the surname was removed, False if it was not in the list. + :rtype: bool + """ + if surname in self.surname_list: + self.surname_list.remove(surname) + return True + else: + return False + + def get_surname_list(self): + """ + Return the list of :class:`~gen.lib.surname.Surname` instances a + ssociated with the object. + + :returns: Returns the list of :class:`~gen.lib.surname.Surname` instances + :rtype: list + """ + return self.surname_list + + def set_surname_list(self, surname_list): + """ + Assign the passed list to the object's list of + :class:`~gen.lib.surname.Surname` instances. + + :param surname_list: List of :class:`~gen.lib.surname.surname` instances + to be associated with the object + :type surname_list: list + """ + self.surname_list = surname_list + + def primary_surname(self): + """ + Return the surname that is the primary surname + + :returns: Returns the surname instance that + is the primary surname. If primary not set, and there is a surname, + the first surname is given, if no surnames, None is returned + :rtype: :class:`~gen.lib.surname.Surname` or None + """ + for surname in surname_list: + if surname.primary: + return surname + if surname_list: + return surname_list[0] + return None + + def _merge_surname_list(self, acquisition): + """ + Merge the list of surname from acquisition with our own. + + :param acquisition: the surname list of this object will be merged with + the current address list. + :rtype acquisition: SurnameBase + """ + surname_list = self.surname_list[:] + for addendum in acquisition.get_surname_list(): + for surname in surname_list: + equi = surname.is_equivalent(addendum) + if equi == IDENTICAL: + break + elif equi == EQUAL: + surname.merge(addendum) + break + else: + self.surname_list.append(addendum) From fa41e63f5ae4e08428e4b204b13de2f1da97926c Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Tue, 14 Sep 2010 22:07:51 +0000 Subject: [PATCH 03/28] Surnamebase, surname and nameorigintype finished svn: r15901 --- src/gen/lib/nameorigintype.py | 87 +++++++++++++++ src/gen/lib/surname.py | 192 ++++++++++++++++++++++++++++++++++ src/gen/lib/surnamebase.py | 17 ++- 3 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 src/gen/lib/nameorigintype.py create mode 100644 src/gen/lib/surname.py diff --git a/src/gen/lib/nameorigintype.py b/src/gen/lib/nameorigintype.py new file mode 100644 index 000000000..7ee51bad4 --- /dev/null +++ b/src/gen/lib/nameorigintype.py @@ -0,0 +1,87 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2007 Donald N. Allingham +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id: nametype.py 14091 2010-01-18 04:42:17Z pez4brian $ + +""" +Name types. +""" + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import sgettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gen.lib.grampstype import GrampsType + +class NameOriginType(GrampsType): + """ + Name Origina Types + + .. attribute UNKNOWN: Unknown origin + .. attribute CUSTOM: Custom user defined origin + .. attribute NONE: no given origin + .. attribute INHERITED: name was inherited from parents + .. attribute GIVEN: name was bestowed on the individual + .. attribute TAKEN: name was chosen by the individual + .. attribute PATRONYMIC: name is derived from father's given name + .. attribute MATRONYMIC: name is derived from mother's given name + .. attribute FEUDAL: name refers to the holding of land in a fief + .. attribute PSEUDONYM: name is fictitious + """ + + UNKNOWN = -1 + CUSTOM = 0 + NONE = 1 + INHERITED = 2 + GIVEN = 3 + TAKEN = 4 + PATRONYMIC = 5 + MATRONYMIC = 6 + FEUDAL = 7 + PSEUDONYM = 8 + + _CUSTOM = CUSTOM + _DEFAULT = NONE + + _DATAMAP = [ + (UNKNOWN , _("Unknown"), "Unknown"), + (CUSTOM , _("Custom"), "Custom"), + (NONE , "", ""), + (INHERITED , _("Surname|Inherited"), "Inherited"), + (GIVEN , _("Surname|Given"), "Given"), + (TAKEN , _("Surname|Taken"), "Taken"), + (PATRONYMIC, _("Patronymic"), "Patronymic"), + (MATRONYMIC, _("Matronymic"), "Matronymic"), + (FEUDAL , _("Surname|Feudal"), "Feudal"), + (PSEUDONYM , _("Pseudonym"), "Pseudonym"), + + ] + + def __init__(self, value=None): + GrampsType.__init__(self, value) + diff --git a/src/gen/lib/surname.py b/src/gen/lib/surname.py new file mode 100644 index 000000000..3da52ebd4 --- /dev/null +++ b/src/gen/lib/surname.py @@ -0,0 +1,192 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2010 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id: name.py 15645 2010-07-22 02:16:32Z dsblank $ + +""" +Surname class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gen.lib.secondaryobj import SecondaryObject +from gen.lib.nameorigintype import NameOriginType +from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT + +#------------------------------------------------------------------------- +# +# Personal Name +# +#------------------------------------------------------------------------- +class Surname(SecondaryObject): + """ + Provide surname information of a name. + + A person may have more that one surname in his name + """ + + def __init__(self, source=None, data=None): + """Create a new Surname instance, copying from the source if provided. + """ + if source: + self.surname = source.surname + self.prefix = source.prefix + self.primary = source.primary + self.origintype = source.origintype + self.connector = source.connector + else: + self.surname = "" + self.prefix = "" + self.primary = False + self.origintype = NameOriginType() + self.connector = "" + if data: + self.unserialize(data) + + def serialize(self): + """ + Convert the object to a serialized tuple of data. + """ + return (self.surname, self.prefix, self.primary, + self.origintype.serialize(), self.connector) + + def is_empty(self): + """ + Indicate if the surname is empty. + """ + return (self.surname == u"" and self.prefix == u"" and + self.connector == u"") + + def unserialize(self, data): + """ + Convert a serialized tuple of data to an object. + """ + (self.surname, self.prefix, self.primary, origin_type, + self.connector) = data + self.origintype = NameOriginType(origin_type) + return self + + def get_text_data_list(self): + """ + Return the list of all textual attributes of the object. + + :returns: Returns the list of all textual attributes of the object. + :rtype: list + """ + return [self.surname, self.prefix, self.connector, + str(self.origintype)] + + def is_equivalent(self, other): + """ + Return if this surname is equivalent, that is agrees in type, surname, + ..., to other. + + :param other: The surname to compare this name to. + :rtype other: Surame + :returns: Constant indicating degree of equivalence. + :rtype: int + """ + # TODO what to do with sort and display? + if self.get_text_data_list() != other.get_text_data_list() or \ + self.primary != other.primary: + return DIFFERENT + else: + if self.is_equal(other): + return IDENTICAL + else: + return EQUAL + + def merge(self, acquisition): + """ + Merge the content of acquisition into this surname. + + Lost: primary, surname, prefix, connector, origintype + + :param acquisition: The surname to merge with the present surname. + :rtype acquisition: Surname + """ + pass + + + def get_surname(self): + """ + Return the surname. + + The surname is one of the not given names coming from the parents + """ + return self.surname + + def set_surname(self, val): + """ + Set the surname. + + The surname is one of the not given names coming from the parents + """ + self.surname = val + + def get_prefix(self): + """ + Return the prefix (or article) of the surname. + + The prefix is not used for sorting or grouping. + """ + return self.prefix + + def set_prefix(self, val): + """ + Set the prefix (or article) of the surname. + + Examples of articles would be 'de' or 'van'. + """ + self.prefix = val + + def set_origintype(self, the_type): + """Set the origin type of the Surname instance.""" + self.origintype.set(the_type) + + def get_origintype(self): + """Return the origin type of the Surname instance.""" + return self.origintype + + def set_connector(self, connector): + """Set the connector for the Surname instance. This defines how a + surname connects to the next surname (eg in Spanish names). + """ + self.connector = connector + + def get_connector(self): + """Get the connector for the Surname instance. This defines how a + surname connects to the next surname (eg in Spanish names). + """ + return self.connector + + def get_primary(self): + """Return if this surname is the primary surname""" + return self.primary + + def set_primary(self, primary): + """Set if this surname is the primary surname.replace + Use :class:`~gen.lib.surname.SurnameBase` to set the primary surname + via :method:`~gen.lib.surname.SurnameBase.set_primary_surname` + """ + self.primary = primary diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py index a4786cb87..c74a97069 100644 --- a/src/gen/lib/surnamebase.py +++ b/src/gen/lib/surnamebase.py @@ -128,13 +128,24 @@ class SurnameBase(object): the first surname is given, if no surnames, None is returned :rtype: :class:`~gen.lib.surname.Surname` or None """ - for surname in surname_list: + for surname in self.surname_list: if surname.primary: return surname - if surname_list: - return surname_list[0] + if self.surname_list: + return self.surname_list[0] return None + def set_primary_surname(self, surnamenr=0): + """ + Set the surname with surnamenr in the surname list as primary surname + Counting starts at 0 + """ + if surnamenr >= len(self.surname_list): + return + for surname in self.surname_list: + surname.set_primary(False) + self.surname_list[surnamenr].set_primary(True) + def _merge_surname_list(self, acquisition): """ Merge the list of surname from acquisition with our own. From 6011cb3ec936185d672f2a07da82306f0d30147d Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 17 Sep 2010 12:18:41 +0000 Subject: [PATCH 04/28] some administrative cleanup svn: r15912 --- src/gen/lib/nameorigintype.py | 4 ++-- src/gen/lib/surname.py | 2 +- src/gen/lib/surnamebase.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gen/lib/nameorigintype.py b/src/gen/lib/nameorigintype.py index 7ee51bad4..00089a607 100644 --- a/src/gen/lib/nameorigintype.py +++ b/src/gen/lib/nameorigintype.py @@ -1,7 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2000-2007 Donald N. Allingham +# Copyright (C) 2010 Benny Malengier # # 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 @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: nametype.py 14091 2010-01-18 04:42:17Z pez4brian $ +# $Id$ """ Name types. diff --git a/src/gen/lib/surname.py b/src/gen/lib/surname.py index 3da52ebd4..221bd44ea 100644 --- a/src/gen/lib/surname.py +++ b/src/gen/lib/surname.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: name.py 15645 2010-07-22 02:16:32Z dsblank $ +# $Id$ """ Surname class for GRAMPS. diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py index c74a97069..d085925fb 100644 --- a/src/gen/lib/surnamebase.py +++ b/src/gen/lib/surnamebase.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: addressbase.py 15645 2010-07-22 02:16:32Z dsblank $ +# $Id$ """ SurnameBase class for GRAMPS. From 84edfee57bc3c639170853b7f984a97e3a0b5bef Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 17 Sep 2010 13:26:36 +0000 Subject: [PATCH 05/28] updated Name object to new structure svn: r15913 --- po/POTFILES.in | 5 +- src/gen/lib/Makefile.am | 3 + src/gen/lib/name.py | 227 ++++++++++++++++--------------------- src/gen/lib/surnamebase.py | 66 ++++++++++- 4 files changed, 168 insertions(+), 133 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 232b0cd99..407c02ffd 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -110,12 +110,15 @@ src/gen/lib/eventref.py src/gen/lib/privsrcnote.py src/gen/lib/placebase.py src/gen/lib/name.py +src/gen/lib/nametype.py +src/gen/lib/nameorigintype.py src/gen/lib/addressbase.py src/gen/lib/family.py src/gen/lib/event.py -src/gen/lib/nametype.py src/gen/lib/secondaryobj.py src/gen/lib/srcbase.py +src/gen/lib/surname.py +src/gen/lib/surnamebase.py src/gen/lib/eventtype.py src/gen/lib/researcher.py src/gen/lib/familyreltype.py diff --git a/src/gen/lib/Makefile.am b/src/gen/lib/Makefile.am index 4bacb685c..fee0a3a65 100644 --- a/src/gen/lib/Makefile.am +++ b/src/gen/lib/Makefile.am @@ -36,6 +36,7 @@ pkgdata_PYTHON = \ mediaref.py \ name.py \ nametype.py \ + nameorigintype.py \ notebase.py \ note.py \ notetype.py \ @@ -57,6 +58,8 @@ pkgdata_PYTHON = \ srcnote.py \ src.py \ srcref.py \ + surname.py \ + surnamebase.py \ styledtext.py \ styledtexttag.py \ styledtexttagtype.py \ diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index e138adf7c..10052aaa9 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.py @@ -35,6 +35,7 @@ from gen.lib.privacybase import PrivacyBase from gen.lib.srcbase import SourceBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase +from gen.lib.surnamebase import SurnameBase from gen.lib.nametype import NameType from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT @@ -43,18 +44,22 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Personal Name # #------------------------------------------------------------------------- -class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): +class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, + DateBase): """ Provide name information about a person. - A person may have more that one name throughout his or her life. + A person may have more that one name throughout his or her life. The Name + object stores one of them """ DEF = 0 # Default format (determined by gramps-wide prefs) - LNFN = 1 # last name first name [patronymic] + LNF = 5 # last name first name FNLN = 2 # first name last name - PTFN = 3 # patronymic first name FN = 4 # first name + #deprecated : + LNFN = 1 # last name first name [patronymic] + PTFN = 3 # patronymic first name def __init__(self, source=None, data=None): """Create a new Name instance, copying from the source if provided. @@ -65,39 +70,34 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): saved differently. """ PrivacyBase.__init__(self, source) + SurnameBase.__init__(self, source) SourceBase.__init__(self, source) NoteBase.__init__(self, source) DateBase.__init__(self, source) if data: (privacy, source_list, note, date, - self.first_name, self.surname, self.suffix, self.title, - name_type, self.prefix, self.patronymic, + self.first_name, surname_list, self.suffix, self.title, name_type, self.group_as, self.sort_as, self.display_as, self.call) = data self.type = NameType(name_type) + SurnameBase.unserialize(self, surname_list) PrivacyBase.unserialize(self, privacy) SourceBase.unserialize(self, source_list) NoteBase.unserialize(self, note) DateBase.unserialize(self, date) elif source: self.first_name = source.first_name - self.surname = source.surname self.suffix = source.suffix self.title = source.title self.type = source.type - self.prefix = source.prefix - self.patronymic = source.patronymic self.group_as = source.group_as self.sort_as = source.sort_as self.display_as = source.display_as self.call = source.call else: self.first_name = "" - self.surname = "" self.suffix = "" self.title = "" self.type = NameType() - self.prefix = "" - self.patronymic = "" self.group_as = "" self.sort_as = self.DEF self.display_as = self.DEF @@ -111,28 +111,32 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): SourceBase.serialize(self), NoteBase.serialize(self), DateBase.serialize(self), - self.first_name, self.surname, self.suffix, self.title, - self.type.serialize(), self.prefix, self.patronymic, + self.first_name, + SurnameBase.serialize(self), + self.suffix, self.title, + self.type.serialize(), self.group_as, self.sort_as, self.display_as, self.call) def is_empty(self): """ Indicate if the name is empty. """ - return (self.first_name == u"" and self.surname == u"" and - self.suffix == u"" and self.title == u"" and - self.prefix == u"" and self.patronymic == u"") + namefieldsempty = (self.first_name == u"" and + self.suffix == u"" and self.title == u"") + surnamefieldsempty = not (False in + [surn.is_empty() for surn in self.surname_list]) + return namefieldsempty and surnamefieldsempty def unserialize(self, data): """ Convert a serialized tuple of data to an object. """ (privacy, source_list, note_list, date, - self.first_name, self.surname, self.suffix, self.title, - name_type, self.prefix, self.patronymic, + self.first_name, surname_list, self.suffix, self.title, name_type, self.group_as, self.sort_as, self.display_as, self.call) = data self.type = NameType(name_type) PrivacyBase.unserialize(self, privacy) + SurnameBase.unserialize(self, surname_list) SourceBase.unserialize(self, source_list) NoteBase.unserialize(self, note_list) DateBase.unserialize(self, date) @@ -145,8 +149,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): :returns: Returns the list of all textual attributes of the object. :rtype: list """ - return [self.first_name, self.surname, self.suffix, self.title, - str(self.type), self.prefix, self.patronymic, self.call] + return [self.first_name, self.suffix, self.title, + str(self.type), self.call] def get_text_data_child_list(self): """ @@ -155,7 +159,7 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return self.source_list + self.surname_list def get_note_child_list(self): """ @@ -189,8 +193,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): def is_equivalent(self, other): """ - Return if this name is equivalent, that is agrees in type, first - call, last, suffix, patronymic, title and date, to other. + Return if this name is equivalent, that is agrees in type, first, + call, surname_list, suffix, title and date, to other. :param other: The name to compare this name to. :rtype other: Name @@ -199,7 +203,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): """ # TODO what to do with sort and display? if self.get_text_data_list() != other.get_text_data_list() or \ - self.get_date_object() != other.get_date_object(): + self.get_date_object() != other.get_date_object() or \ + SurnameBase.serialize(self) != SurnameBase.serialize(other): return DIFFERENT else: if self.is_equal(other): @@ -210,8 +215,10 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): def merge(self, acquisition): """ Merge the content of acquisition into this name. + Normally the person merge code should opt for adding an alternate + name if names are actually different (like not equal surname list) - Lost: type, first, call, last, suffix, patronymic, title and date of + Lost: type, first, call, suffix, title and date of acquisition. :param acquisition: The name to merge with the present name. @@ -219,6 +226,7 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): """ # TODO what to do with sort and display? self._merge_privacy(acquisition) + self._merge_surname_list(acquisition) self._merge_note_list(acquisition) self._merge_source_reference_list(acquisition) @@ -305,22 +313,6 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): """ self.call = val - def get_surname_prefix(self): - """ - Return the prefix (or article) of a surname. - - The prefix is not used for sorting or grouping. - """ - return self.prefix - - def set_surname_prefix(self, val): - """ - Set the prefix (or article) of a surname. - - Examples of articles would be 'de' or 'van'. - """ - self.prefix = val - def set_type(self, the_type): """Set the type of the Name instance.""" self.type.set(the_type) @@ -333,33 +325,13 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): """Set the given name for the Name instance.""" self.first_name = name - def set_patronymic(self, name): - """Set the patronymic name for the Name instance.""" - self.patronymic = name - - def set_surname(self, name): - """Set the surname (or last name) for the Name instance.""" - self.surname = name - - def set_suffix(self, name): - """Set the suffix (such as Jr., III, etc.) for the Name instance.""" - self.suffix = name - def get_first_name(self): """Return the given name for the Name instance.""" return self.first_name - def get_patronymic(self): - """Return the patronymic name for the Name instance.""" - return self.patronymic - - def get_surname(self): - """Return the surname (or last name) for the Name instance.""" - return self.surname - - def get_upper_surname(self): - """Return the surname (or last name) for the Name instance.""" - return self.surname.upper() + def set_suffix(self, name): + """Set the suffix (such as Jr., III, etc.) for the Name instance.""" + self.suffix = name def get_suffix(self): """Return the suffix for the Name instance.""" @@ -376,85 +348,53 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): def get_name(self): """ Return a name string built from the components of the Name instance, - in the form of surname, Firstname. + in the form of: surname, Firstname. """ - - if self.patronymic: - first = "%s %s" % (self.first_name, self.patronymic) - else: - first = self.first_name + first = self.first_name + surname = self.get_surname() if self.suffix: - if self.prefix: - return "%s %s, %s %s" % (self.prefix, self.surname, - first, self.suffix) - else: - return "%s, %s %s" % (self.surname, first, self.suffix) + return "%s, %s %s" % (surname, first, self.suffix) else: - if self.prefix: - return "%s %s, %s" % (self.prefix, self.surname, first) - else: - return "%s, %s" % (self.surname, first) + return "%s, %s" % (surname, first) def get_upper_name(self): """ Return a name string built from the components of the Name instance, - in the form of surname, Firstname. + in the form of SURNAME, Firstname. """ - - if self.patronymic: - first = "%s %s" % (self.first_name, self.patronymic) - else: - first = self.first_name + first = self.first_name + surname = self.get_surname().upper() if self.suffix: - if self.prefix: - return "%s %s, %s %s" % (self.prefix.upper(), - self.surname.upper(), first, - self.suffix) - else: - return "%s, %s %s" % (self.surname.upper(), first, self.suffix) + return "%s, %s %s" % (surname, first, self.suffix) else: - if self.prefix: - return "%s %s, %s" % (self.prefix.upper(), - self.surname.upper(), - first) - else: - return "%s, %s" % (self.surname.upper(), first) + return "%s, %s" % (surname, first) def get_regular_name(self): """ Return a name string built from the components of the Name instance, in the form of Firstname surname. """ - if self.patronymic: - first = "%s %s" % (self.first_name, self.patronymic) - else: - first = self.first_name + first = self.first_name + surname = self.get_surname() if (self.suffix == ""): - if self.prefix: - return "%s %s %s" % (first, self.prefix, self.surname) - else: - return "%s %s" % (first, self.surname) + return "%s %s" % (first, surname) else: - if self.prefix: - return "%s %s %s, %s" % (first, self.prefix, self.surname, - self.suffix) - else: - return "%s %s, %s" % (first, self.surname, self.suffix) + return "%s %s, %s" % (first, surname, self.suffix) def get_gedcom_parts(self): """ Returns a GEDCOM-formatted name dictionary. + Note, field patronymic and prefix are deprecated, prefix_list and + surname list, added. """ retval = {} retval['given'] = self.first_name.strip() - retval['patronymic'] = self.patronymic.strip() - if retval['patronymic']: - retval['given'] = "%s %s" % (retval['given'], - retval['patronymic']) - retval['surname'] = self.surname.replace('/', '?') - retval['prefix'] = self.prefix.replace('/', '?') + retval['surname'] = self.get_surname().replace('/', '?') retval['suffix'] = self.suffix retval['title'] = self.title + retval['surnamelist'] = self.get_surnames() + retval['prefixes'] = self.get_prefixes() + retval['connectors'] = self.get_connectors() return retval def get_gedcom_name(self): @@ -462,21 +402,46 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): Returns a GEDCOM-formatted name. """ firstname = self.first_name.strip() - patron = self.patronymic.strip() - if patron: - firstname = "%s %s" % (firstname, patron) - surname = self.surname.replace('/', '?') - surprefix = self.prefix.replace('/', '?') + surname = self.get_surname().replace('/', '?') suffix = self.suffix title = self.title if suffix == "": - if surprefix == "": - return '%s /%s/' % (firstname, surname) - else: - return '%s /%s %s/' % (firstname, surprefix, surname) - elif surprefix == "": - return '%s /%s/ %s' % (firstname, surname, suffix) + return '%s /%s/' % (firstname, surname) else: - return '%s /%s %s/ %s' % (firstname, surprefix, surname, suffix) - - + return '%s /%s/ %s' % (firstname, surname, suffix) + +## +## #DEPRECATED METHODS +## +## +## def get_surname_prefix(self): +## """ +## Return the prefix (or article) of a surname. +## +## The prefix is not used for sorting or grouping. +## """ +## return self.prefix +## +## def set_surname_prefix(self, val): +## """ +## Set the prefix (or article) of a surname. +## +## Examples of articles would be 'de' or 'van'. +## """ +## self.prefix = val +## +## def get_patronymic(self): +## """Return the patronymic name for the Name instance.""" +## return self.patronymic +## +## def set_patronymic(self, name): +## """Set the patronymic name for the Name instance.""" +## self.patronymic = name +## +## def get_surname(self): +## """Return the surname (or last name) for the Name instance.""" +## return self.surname +## +## def set_surname(self, name): +## """Set the surname (or last name) for the Name instance.""" +## self.surname = name diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py index d085925fb..f92bf144a 100644 --- a/src/gen/lib/surnamebase.py +++ b/src/gen/lib/surnamebase.py @@ -24,6 +24,8 @@ SurnameBase class for GRAMPS. """ +from gen.ggettext import gettext as _ + #------------------------------------------------------------------------- # # GRAMPS modules @@ -149,9 +151,12 @@ class SurnameBase(object): def _merge_surname_list(self, acquisition): """ Merge the list of surname from acquisition with our own. + This method is normally only called when surnames are equal, if they + are different, the merge code should fall back to storing an + alternate name. For completeness, the code is present nevertheless. :param acquisition: the surname list of this object will be merged with - the current address list. + the current surname list. :rtype acquisition: SurnameBase """ surname_list = self.surname_list[:] @@ -161,7 +166,66 @@ class SurnameBase(object): if equi == IDENTICAL: break elif equi == EQUAL: + #This should normally never happen, an alternate name + # should be added surname.merge(addendum) break else: self.surname_list.append(addendum) + + def get_surname(self): + """ + Return a fully formatted surname utilizing the surname_list + """ + totalsurn = "" + for surn in self.surname_list: + partsurn = surn.get_surname() + if surn.get_prefix(): + fsurn = _('%(first)s %(second)s') % {'first': surn.get_prefix(), + 'second': partsurn} + else: + fsurn = partsurn + fsurn = fsurn.strip() + if surn.get_connector(): + fsurn = _('%(first)s %(second)s') % {'first': fsurn, + 'second': surn.get_connector()} + fsurn = fsurn.strip() + totalsurn = _('%(first)s %(second)s') % {'first': totalsurn, + 'second': fsurn} + return totalsurn.strip() + + + def get_upper_surname(self): + """Return a fully formatted surname capitalized""" + return self.get_surname().upper() + + def get_surnames(self): + """ + Return a list of surnames (no prefix or connectors) + """ + surnl = [] + for surn in self.surname_list: + realsurn = surn.get_surname() + if realsurn: + surnl.append(realsurn) + + def get_prefixes(self): + """ + Return a list of prefixes + """ + prefixl = [] + for surn in self.surname_list: + prefix = surn.get_prefix() + if prefix: + prefixl.append(prefix) + + def get_connectors(self): + """ + Return a list of surnames (no prefix or connectors) + """ + connl = [] + for surn in self.surname_list: + conn = surn.get_connector() + if conn: + connl.append(conn) + \ No newline at end of file From bb248fadcea95dce3dd97b9fba97887afaf81c3d Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 17 Sep 2010 22:13:31 +0000 Subject: [PATCH 06/28] Good progress: * name displayer updated to new structure * person view coded updated * small change in dbloader so errors can be seen svn: r15914 --- src/gen/db/upgrade.py | 93 ++++++- src/gen/display/name.py | 323 +++++++++++++++++------- src/gen/lib/__init__.py | 2 + src/gen/lib/name.py | 5 +- src/gen/lib/surnamebase.py | 4 +- src/gui/dbloader.py | 12 +- src/gui/views/treemodels/peoplemodel.py | 3 +- 7 files changed, 332 insertions(+), 110 deletions(-) diff --git a/src/gen/db/upgrade.py b/src/gen/db/upgrade.py index b166d16c2..85a769aed 100644 --- a/src/gen/db/upgrade.py +++ b/src/gen/db/upgrade.py @@ -21,26 +21,77 @@ # $Id$ from __future__ import with_statement -from gen.db import BSDDBTxn + """ -upgrade +methods to upgrade a database from version 13 to current version """ +from gen.db import BSDDBTxn +from gen.lib.nameorigintype import NameOriginType + + def gramps_upgrade_15(self): - """Upgrade database from version 14 to 15.""" - # This upgrade adds tagging + """Upgrade database from version 14 to 15. This upgrade adds: + * tagging + * surname list + """ length = len(self.person_map) self.set_total(length) # --------------------------------- # Modify Person # --------------------------------- - # Append the new tag field for handle in self.person_map.keys(): person = self.person_map[handle] - new_person = list(person) - new_person.append([]) - new_person = tuple(new_person) + (junk_handle, # 0 + gramps_id, # 1 + gender, # 2 + primary_name, # 3 + alternate_names, # 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + urls, # 13 + ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + pprivate, # 19 + person_ref_list, # 20 + ) = person + + new_primary_name = convert_name_15(primary_name) + new_alternate_names = [convert_name_15(altname) for altname in + alternate_names] + new_person = (junk_handle, # 0 + gramps_id, # 1 + gender, # 2 + new_primary_name, # 3 + new_alternate_names,# 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + urls, # 13 + ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + pprivate, # 19 + person_ref_list, # 20 + [] # 21, tags + ) with BSDDBTxn(self.env, self.person_map) as txn: txn.put(str(handle), new_person) self.update() @@ -49,6 +100,32 @@ def gramps_upgrade_15(self): with BSDDBTxn(self.env, self.metadata) as txn: txn.put('version', 15) +def convert_name_15(name): + (privacy, source_list, note_list, date, + first_name, surname, suffix, title, + name_type, prefix, patronymic, + group_as, sort_as, display_as, call) = name + + connector = u"" + origintype = (NameOriginType.NONE, u"") + patorigintype = (NameOriginType.PATRONYMIC, u"") + + if patronymic.strip() == u"": + #no patronymic, create a single surname + surname_list = [(surname, prefix, True, origintype, connector)] + else: + #a patronymic, if no surname or equal as patronymic, a single surname + if (surname.strip() == u"") or (surname == patronymic and prefix == u""): + surname_list = [(patronymic, prefix, True, patorigintype, connector)] + else: + #two surnames, first patronymic, then surname which is primary + surname_list = [(patronymic, u"", False, patorigintype, u""), + (surname, prefix, True, origintype, connector)] + + return (privacy, source_list, note_list, date, + first_name, surname_list, suffix, title, name_type, + group_as, sort_as, display_as, call) + def gramps_upgrade_14(self): """Upgrade database from version 13 to 14.""" # This upgrade modifies notes and dates diff --git a/src/gen/display/name.py b/src/gen/display/name.py index 2aca5c56f..3fc52f15d 100644 --- a/src/gen/display/name.py +++ b/src/gen/display/name.py @@ -23,6 +23,20 @@ """ Class handling language-specific displaying of names. + +Specific symbols for parts of a name are defined: + 't' : title + 'f' : given (first names) + 'l' : full surname (lastname) + 'c' : callname + 'x' : callname if existing, otherwise first first name (common name) + 'i' : initials of the first names + 'f' : patronymic surname (father) + 'o' : surnames without patronymic + 'm' : primary surname (main) + 'p' : list of all prefixes + 'q' : surnames without prefixes and connectors + 's' : suffix """ #------------------------------------------------------------------------- @@ -38,7 +52,7 @@ import re # GRAMPS modules # #------------------------------------------------------------------------- -from gen.lib import Name +from gen.lib import Name, NameOriginType try: import config @@ -52,17 +66,23 @@ except ImportError: # Constants # #------------------------------------------------------------------------- -_FIRSTNAME = 4 -_SURNAME = 5 -_SUFFIX = 6 -_TITLE = 7 -_TYPE = 8 -_PREFIX = 9 -_PATRONYM = 10 -_GROUP = 11 -_SORT = 12 -_DISPLAY = 13 -_CALL = 14 +_FIRSTNAME = 4 +_SURNAME_LIST = 5 +_SUFFIX = 6 +_TITLE = 7 +_TYPE = 8 +#_PREFIX = 9 +#_PATRONYM = 10 +_GROUP = 11 +_SORT = 12 +_DISPLAY = 13 +_CALL = 14 +_SURNAME_IN_LIST = 0 +_PREFIX_IN_LIST = 1 +_PRIMARY_IN_LIST = 2 +_TYPE_IN_LIST = 3 +_CONNECTOR_IN_LIST = 4 +_ORIGINPATRO = NameOriginType.PATRONYMIC _ACT = True _INA = False @@ -97,6 +117,63 @@ class NameDisplayError(Exception): def __str__(self): return self.value +#------------------------------------------------------------------------- +# +# Functions to extract data from raw lists (unserialized objects) +# +#------------------------------------------------------------------------- + +def _raw_full_surname(raw_surn_data_list): + """method for the 'l' symbol: full surnames""" + result = "" + for raw_surn_data in raw_surn_data_list: + result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST], + raw_surn_data[_SURNAME_IN_LIST], + raw_surn_data[_CONNECTOR_IN_LIST]) + return ' '.join(result.split()).strip() + +def _raw_primary_surname(raw_surn_data_list): + """method for the 'm' symbol: primary surname""" + for raw_surn_data in raw_surn_data_list: + if raw_surn_data[_PRIMARY_IN_LIST]: + result = "%s %s" % (raw_surn_data[_PREFIX_IN_LIST], + raw_surn_data[_SURNAME_IN_LIST]) + return ' '.join(result.split()) + return '' + +def _raw_patro_surname(raw_surn_data_list): + """method for the 'f' symbol: patronymic surname""" + for raw_surn_data in raw_surn_data_list: + if raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO: + result = "%s %s" % (raw_surn_data[_PREFIX_IN_LIST], + raw_surn_data[_SURNAME_IN_LIST]) + return ' '.join(result.split()) + return '' + +def _raw_nonpatro_surname(raw_surn_data_list): + """method for the 'o' symbol: full surnames without patronymic""" + result = "" + for raw_surn_data in raw_surn_data_list: + if raw_surn_data[_TYPE_IN_LIST][0] != _ORIGINPATRO: + result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST], + raw_surn_data[_SURNAME_IN_LIST], + raw_surn_data[_CONNECTOR_IN_LIST]) + return ' '.join(result.split()).strip() + +def _raw_prefix_surname(raw_surn_data_list): + """method for the 'p' symbol: all prefixes""" + result = "" + for raw_surn_data in raw_surn_data_list: + result += "%s " % (raw_surn_data[_PREFIX_IN_LIST]) + return ' '.join(result.split()).strip() + +def _raw_single_surname(raw_surn_data_list): + """method for the 'q' symbol: surnames without prefix and connectors""" + result = "" + for raw_surn_data in raw_surn_data_list: + result += "%s " % (raw_surn_data[_SURNAME_IN_LIST]) + return ' '.join(result.split()).strip() + #------------------------------------------------------------------------- # # NameDisplay class @@ -112,10 +189,11 @@ class NameDisplay(object): STANDARD_FORMATS = [ (Name.DEF,_("Default format (defined by Gramps preferences)"),'',_ACT), - (Name.LNFN,_("Surname, Given Patronymic"),'%p %l, %f %y %s',_ACT), - (Name.FNLN,_("Given Surname"),'%f %y %p %l %s',_ACT), - (Name.PTFN,_("Patronymic, Given"),'%p %y, %s %f',_ACT), - (Name.FN,_("Given"),'%f',_ACT) + (Name.LNFN,_("Surname, Given"),'%p %l, %f %s',_ACT), + (Name.FN,_("Given"),'%f',_ACT), + (Name.FNLN,_("Given Surname"),'%f %p %l %s',_ACT), + # DEPRECATED FORMATS + (Name.PTFN,_("Patronymic, Given"),'%p %y, %s %f',_INA), ] def __init__(self): @@ -125,11 +203,12 @@ class NameDisplay(object): if WITH_GRAMPS_CONFIG: self.default_format = config.get('preferences.name-format') - if self.default_format == 0: + if self.default_format == 0 \ + or self.default_format not in Name.NAMEFORMATS : self.default_format = Name.LNFN config.set('preferences.name-format', self.default_format) else: - self.default_format = 1 + self.default_format = Name.LNFN self.set_default_format(self.default_format) @@ -138,28 +217,17 @@ class NameDisplay(object): def _format_raw_fn(self, fmt_str): return lambda x: self.format_str_raw(x, fmt_str) - + def _raw_lnfn(self, raw_data): - result = "%s %s, %s %s %s" % (raw_data[_PREFIX], - raw_data[_SURNAME], - raw_data[_FIRSTNAME], - raw_data[_PATRONYM], - raw_data[_SUFFIX]) + result = "%s, %s %s" % (_raw_full_surname(raw_data[_SURNAME_LIST]), + raw_data[_FIRSTNAME], + raw_data[_SUFFIX]) return ' '.join(result.split()) def _raw_fnln(self, raw_data): - result = "%s %s %s %s %s" % (raw_data[_FIRSTNAME], - raw_data[_PATRONYM], - raw_data[_PREFIX], - raw_data[_SURNAME], - raw_data[_SUFFIX]) - return ' '.join(result.split()) - - def _raw_ptfn(self, raw_data): - result = "%s %s, %s %s" % (raw_data[_PREFIX], - raw_data[_PATRONYM], - raw_data[_SUFFIX], - raw_data[_FIRSTNAME]) + result = "%s %s %s" % (raw_data[_FIRSTNAME], + _raw_full_surname(raw_data[_SURNAME_LIST]), + raw_data[_SUFFIX]) return ' '.join(result.split()) def _raw_fn(self, raw_data): @@ -170,7 +238,6 @@ class NameDisplay(object): raw_func_dict = { Name.LNFN : self._raw_lnfn, Name.FNLN : self._raw_fnln, - Name.PTFN : self._raw_ptfn, Name.FN : self._raw_fn, } @@ -279,31 +346,59 @@ class NameDisplay(object): The new function is of the form: def fn(raw_data): - return "%s %s %s %s %s" % (raw_data[_TITLE], + return "%s %s %s" % (raw_data[_TITLE], raw_data[_FIRSTNAME], - raw_data[_PREFIX], - raw_data[_SURNAME], raw_data[_SUFFIX]) + Specific symbols for parts of a name are defined: + 't' : title + 'f' : given (first names) + 'l' : full surname (lastname) + 'c' : callname + 'x' : callname if existing, otherwise first first name (common name) + 'i' : initials of the first names + 'f' : patronymic surname (father) + 'o' : surnames without patronymic + 'm' : primary surname (main) + 'p' : list of all prefixes + 'q' : surnames without prefixes and connectors + 's' : suffix + """ # we need the names of each of the variables or methods that are # called to fill in each format flag. # Dictionary is "code": ("expression", "keyword", "i18n-keyword") - d = {"t": ("raw_data[_TITLE]", "title", _("Person|title")), - "f": ("raw_data[_FIRSTNAME]", "given", _("given")), - "p": ("raw_data[_PREFIX]", "prefix", _("prefix")), - "l": ("raw_data[_SURNAME]", "surname", _("surname")), - "s": ("raw_data[_SUFFIX]", "suffix", _("suffix")), - "y": ("raw_data[_PATRONYM]", "patronymic", _("patronymic")), - "c": ("raw_data[_CALL]", "call", _("call")), + d = {"t": ("raw_data[_TITLE]", "title", + _("String replacement keyword Person|title")), + "f": ("raw_data[_FIRSTNAME]", "given", + _("String replacement keyword|given")), + "l": ("_raw_full_surname(raw_data[_SURNAME_LIST])", "surname", + _("String replacement keyword|surname")), + "s": ("raw_data[_SUFFIX]", "suffix", + _("String replacement keyword|suffix")), + "c": ("raw_data[_CALL]", "call", + _("String replacement keyword|call")), "x": ("(raw_data[_CALL] or raw_data[_FIRSTNAME].split(' ')[0])", - "common", - _("common")), + "common", + _("String replacement keyword|common")), "i": ("''.join([word[0] +'.' for word in ('. ' +" + " raw_data[_FIRSTNAME]).split()][1:])", - "initials", - _("initials")) + "initials", + _("String replacement keyword|initials")), + "f": ("_raw_patro_surname(raw_data[_SURNAME_LIST])", "patronymic", + _("String replacement keyword|patronymic")), + "o": ("_raw_nonpatro_surname(raw_data[_SURNAME_LIST])", "notpatronymic", + _("String replacement keyword|notpatronymic")), + "m": ("_raw_primary_surname(raw_data[_SURNAME_LIST])", + "primarysurname", + _("String replacement keyword|primarysurname")), + "p": ("_raw_prefix_surname(raw_data[_SURNAME_LIST])", + "prefix", + _("String replacement keyword|prefix")), + "q": ("_raw_single_surname(raw_data[_SURNAME_LIST])", + "rawsurnames", + _("String replacement keyword|rawsurnames")), } args = "raw_data" return self._make_fn(format_str, d, args) @@ -321,26 +416,54 @@ class NameDisplay(object): The new function is of the form: - def fn(first,surname,prefix,suffix,patronymic,title,call,): - return "%s %s %s %s %s" % (first,surname,prefix,suffix,patronymic) - + def fn(first, raw_surname_list, suffix, title, call,): + return "%s %s" % (first,suffix) + + Specific symbols for parts of a name are defined: + 't' : title + 'f' : given (first names) + 'l' : full surname (lastname) + 'c' : callname + 'x' : callname if existing, otherwise first first name (common name) + 'i' : initials of the first names + 'f' : patronymic surname (father) + 'o' : surnames without patronymic + 'm' : primary surname (main) + 'p' : list of all prefixes + 'q' : surnames without prefixes and connectors + 's' : suffix """ # we need the names of each of the variables or methods that are # called to fill in each format flag. # Dictionary is "code": ("expression", "keyword", "i18n-keyword") - d = {"t": ("title", "title", _("Person|title")), - "f": ("first", "given", _("given")), - "p": ("prefix", "prefix", _("prefix")), - "l": ("surname", "surname", _("surname")), - "s": ("suffix", "suffix", _("suffix")), - "y": ("patronymic", "patronymic", _("patronymic")), - "c": ("call", "call", _("call")), - "x": ("(call or first.split(' ')[0])", "common", _("common")), + d = {"t": ("title", "title", + _("String replacement keyword Person|title")), + "f": ("first", "given", + _("String replacement keyword|given")), + "l": ("_raw_full_surname(raw_surname_list)", "surname", + _("String replacement keyword|surname")), + "s": ("suffix", "suffix", + _("String replacement keyword|suffix")), + "c": ("call", "call", + _("String replacement keyword|call")), + "x": ("(call or first.split(' ')[0])", "common", + _("String replacement keyword|common")), "i": ("''.join([word[0] +'.' for word in ('. ' + first).split()][1:])", - "initials", _("initials")) + "initials", + _("String replacement keyword|initials")), + "f": ("_raw_patro_surname(raw_surname_list)", "patronymic", + _("String replacement keyword|patronymic")), + "o": ("_raw_nonpatro_surname(raw_surname_list)", "notpatro", + _("String replacement keyword|notpatro")), + "m": ("_raw_primary_surname(raw_surname_list)", "primary", + _("String replacement keyword name|primary")), + "p": ("_raw_prefix_surname(raw_surname_list)", "prefix", + _("String replacement keyword|prefix")), + "q": ("_raw_single_surname(raw_surname_list)", "rawlastnames", + _("String replacement keyword|rawlastnames")), } - args = "first,surname,prefix,suffix,patronymic,title,call" + args = "first,raw_surname_list,suffix,title,call" return self._make_fn(format_str, d, args) def _make_fn(self, format_str, d, args): @@ -410,7 +533,7 @@ class NameDisplay(object): # find each format flag in the original format string # for each one we find the variable name that is needed to - # replace it and add this to a list. This list will be used + # replace it and add this to a list. This list will be used to # generate the replacement tuple. # This compiled pattern should match all of the format codes. @@ -442,9 +565,9 @@ def fn(%s): return fn def format_str(self, name, format_str): - return self._format_str_base(name.first_name, name.surname, name.prefix, - name.suffix, name.patronymic, name.title, - name.call,format_str) + return self._format_str_base(name.first_name, name.surname_list, + name.suffix, name.title, + name.call, format_str) def format_str_raw(self, raw_data, format_str): """ @@ -463,22 +586,25 @@ def fn(%s): return ' '.join(s.split()) - def _format_str_base(self, first, surname, prefix, suffix, patronymic, - title, call, format_str): + def _format_str_base(self, first, surname_list, suffix, title, call, + format_str): """ Generates name from a format string. The following substitutions are made: - %t -> title - %f -> given (first name) - %p -> prefix - %s -> suffix - %l -> surname (last name) - %y -> patronymic - %c -> call - %x -> common - %i -> initials - The capital letters are substituted for capitalized name components. + '%t' : title + '%f' : given (first names) + '%l' : full surname (lastname) + '%c' : callname + '%x' : callname if existing, otherwise first first name (common name) + '%i' : initials of the first names + '%f' : patronymic surname (father) + '%o' : surnames without patronymic + '%m' : primary surname (main) + '%p' : list of all prefixes + '%q' : surnames without prefixes and connectors + '%s' : suffix + The capital letters are substituted for capitalized name components. The %% is substituted with the single % character. All the other characters in the fmt_str are unaffected. """ @@ -487,7 +613,8 @@ def fn(%s): func = self._gen_cooked_func(format_str) self.__class__.format_funcs[format_str] = func try: - s = func(first,surname,prefix,suffix,patronymic,title,call) + s = func(first, [surn.serialize() for surn in surname_list], + suffix, title, call) except (ValueError, TypeError,): raise NameDisplayError, "Incomplete format string" @@ -496,7 +623,8 @@ def fn(%s): #------------------------------------------------------------------------- def sort_string(self, name): - return u"%-25s%-30s%s" % (name.surname, name.first_name, name.suffix) + return u"%-25s%-30s%s" % (name.get_primary_surname, name.first_name, + name.suffix) def sorted(self, person): """ @@ -560,7 +688,7 @@ def fn(%s): def display_formal(self, person): """ Return a text string representing the L{gen.lib.Person} instance's - L{Name} in a manner that should be used for normal displaying. + L{Name} in a manner that should be used for formal displaying. @param person: L{gen.lib.Person} instance that contains the L{Name} that is to be displayed. The primary name is used for @@ -590,7 +718,7 @@ def fn(%s): return self.name_formats[num][_F_FN](name) def display_given(self, person): - return self.format_str(person.get_primary_name(),'%f %y') + return self.format_str(person.get_primary_name(),'%f') def name_grouping(self, db, person): return self.name_grouping_name(db, person.primary_name) @@ -599,22 +727,29 @@ def fn(%s): if pn.group_as: return pn.group_as sv = pn.sort_as - if sv == Name.LNFN or sv == Name.DEF: - return db.get_name_group_mapping(pn.surname) - elif sv == Name.PTFN: - return db.get_name_group_mapping(pn.patronymic) - else: + if sv == Name.DEF: + return db.get_name_group_mapping(pn.get_primary_surname()) + elif sv == Name.LNFN: + return db.get_name_group_mapping(pn.get_surname()) + elif sv == Name.FN: return db.get_name_group_mapping(pn.first_name) + else: + return db.get_name_group_mapping(pn.get_primary_surname()) def name_grouping_data(self, db, pn): if pn[_GROUP]: return pn[_GROUP] sv = pn[_SORT] - if sv == Name.LNFN or sv == Name.DEF: - return db.get_name_group_mapping(pn[_SURNAME]) - elif sv == Name.PTFN: - return db.get_name_group_mapping(pn[_PATRONYM]) - else: + if sv == Name.DEF: + return db.get_name_group_mapping(_raw_primary_surname( + pn[_SURNAME_LIST])) + elif sv == Name.LNFN: + return db.get_name_group_mapping(_raw_full_surname( + pn[_SURNAME_LIST])) + elif sv == Name.FN: return db.get_name_group_mapping(pn[_FIRSTNAME]) + else: + return db.get_name_group_mapping(_raw_primary_surname( + pn[_SURNAME_LIST])) displayer = NameDisplay() diff --git a/src/gen/lib/__init__.py b/src/gen/lib/__init__.py index f7b486e1b..dcbe78558 100644 --- a/src/gen/lib/__init__.py +++ b/src/gen/lib/__init__.py @@ -36,6 +36,7 @@ from gen.lib.mediaref import MediaRef from gen.lib.name import Name from gen.lib.reporef import RepoRef from gen.lib.srcref import SourceRef +from gen.lib.surname import Surname from gen.lib.url import Url from gen.lib.witness import Witness from gen.lib.childref import ChildRef @@ -68,6 +69,7 @@ from gen.lib.familyreltype import FamilyRelType from gen.lib.srcmediatype import SourceMediaType from gen.lib.eventroletype import EventRoleType from gen.lib.markertype import MarkerType +from gen.lib.nameorigintype import NameOriginType from gen.lib.notetype import NoteType from gen.lib.styledtexttagtype import StyledTextTagType diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index 10052aaa9..c103c8865 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.py @@ -54,11 +54,12 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, """ DEF = 0 # Default format (determined by gramps-wide prefs) - LNF = 5 # last name first name + LNFN = 1 # last name first name FNLN = 2 # first name last name FN = 4 # first name + + NAMEFORMATS = (DEF, LNFN, FNLN, FN) #deprecated : - LNFN = 1 # last name first name [patronymic] PTFN = 3 # patronymic first name def __init__(self, source=None, data=None): diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py index f92bf144a..7b570e9e9 100644 --- a/src/gen/lib/surnamebase.py +++ b/src/gen/lib/surnamebase.py @@ -121,9 +121,9 @@ class SurnameBase(object): """ self.surname_list = surname_list - def primary_surname(self): + def get_primary_surname(self): """ - Return the surname that is the primary surname + Return the string of the surname that is the primary surname :returns: Returns the surname instance that is the primary surname. If primary not set, and there is a surname, diff --git a/src/gui/dbloader.py b/src/gui/dbloader.py index b79c20f46..13aae5671 100644 --- a/src/gui/dbloader.py +++ b/src/gui/dbloader.py @@ -86,7 +86,14 @@ class DbLoader(CLIDbLoader): return 1 def _dberrordialog(self, msg): - DBErrorDialog(str(msg.value)) + import traceback + exc = traceback.format_exc() + try: + DBErrorDialog(str(msg.value)) + _LOG.error(str(msg.value)) + except: + DBErrorDialog(str(msg)) + _LOG.error(str(msg) +"\n" + exc) def _begin_progress(self): self.uistate.set_busy_cursor(1) @@ -312,8 +319,9 @@ class DbLoader(CLIDbLoader): except Errors.DbError, msg: self.dbstate.no_database() self._dberrordialog(msg) - except Exception: + except Exception as newerror: self.dbstate.no_database() + self._dberrordialog(str(newerror)) self._end_progress() return True diff --git a/src/gui/views/treemodels/peoplemodel.py b/src/gui/views/treemodels/peoplemodel.py index c346e9c94..08b628ac0 100644 --- a/src/gui/views/treemodels/peoplemodel.py +++ b/src/gui/views/treemodels/peoplemodel.py @@ -202,7 +202,7 @@ class PeopleBaseModel(object): def sort_name(self, data): n = Name() n.unserialize(data[COLUMN_NAME]) - return (n.get_surname(), n.get_first_name()) + return (n.get_primary_surname().get_surname(), n.get_first_name()) def column_name(self, data): handle = data[0] @@ -515,7 +515,6 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel): data The object data. """ ngn = name_displayer.name_grouping_data - nsn = name_displayer.raw_sorted_name name_data = data[COLUMN_NAME] group_name = ngn(self.db, name_data) From f111dcbad9ea98d9128e68becdc4ec71995c150a Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 25 Sep 2010 15:11:54 +0000 Subject: [PATCH 07/28] add nick and famnick svn: r15930 --- src/gen/db/upgrade.py | 3 +- src/gen/display/name.py | 37 +++++++++++++++------ src/gen/lib/name.py | 58 +++++++++++++++++++++++++++++---- src/plugins/import/ImportXml.py | 1 + src/plugins/lib/libgrampsxml.py | 2 +- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/gen/db/upgrade.py b/src/gen/db/upgrade.py index 85a769aed..be383ad2e 100644 --- a/src/gen/db/upgrade.py +++ b/src/gen/db/upgrade.py @@ -122,9 +122,10 @@ def convert_name_15(name): surname_list = [(patronymic, u"", False, patorigintype, u""), (surname, prefix, True, origintype, connector)] + #return new value, add two empty strings for nick and family nick return (privacy, source_list, note_list, date, first_name, surname_list, suffix, title, name_type, - group_as, sort_as, display_as, call) + group_as, sort_as, display_as, call, u"", u"") def gramps_upgrade_14(self): """Upgrade database from version 13 to 14.""" diff --git a/src/gen/display/name.py b/src/gen/display/name.py index 3fc52f15d..9367249bb 100644 --- a/src/gen/display/name.py +++ b/src/gen/display/name.py @@ -37,6 +37,8 @@ Specific symbols for parts of a name are defined: 'p' : list of all prefixes 'q' : surnames without prefixes and connectors 's' : suffix + 'n' : nick name + 'g' : family nick name """ #------------------------------------------------------------------------- @@ -71,12 +73,12 @@ _SURNAME_LIST = 5 _SUFFIX = 6 _TITLE = 7 _TYPE = 8 -#_PREFIX = 9 -#_PATRONYM = 10 -_GROUP = 11 -_SORT = 12 -_DISPLAY = 13 -_CALL = 14 +_GROUP = 9 +_SORT = 10 +_DISPLAY = 11 +_CALL = 12 +_NICK = 13 +_FAMNICK = 14 _SURNAME_IN_LIST = 0 _PREFIX_IN_LIST = 1 _PRIMARY_IN_LIST = 2 @@ -363,6 +365,8 @@ class NameDisplay(object): 'p' : list of all prefixes 'q' : surnames without prefixes and connectors 's' : suffix + 'n' : nick name + 'g' : family nick name """ @@ -399,6 +403,10 @@ class NameDisplay(object): "q": ("_raw_single_surname(raw_data[_SURNAME_LIST])", "rawsurnames", _("String replacement keyword|rawsurnames")), + "n": ("raw_data[_NICK]", "nickname", + _("String replacement keyword|nickname")), + "g": ("raw_data[_FAMNICK]", "famnick", + _("String replacement keyword|famnick")), } args = "raw_data" return self._make_fn(format_str, d, args) @@ -432,6 +440,8 @@ class NameDisplay(object): 'p' : list of all prefixes 'q' : surnames without prefixes and connectors 's' : suffix + 'n' : nick name + 'g' : family nick name """ # we need the names of each of the variables or methods that are @@ -462,8 +472,12 @@ class NameDisplay(object): _("String replacement keyword|prefix")), "q": ("_raw_single_surname(raw_surname_list)", "rawlastnames", _("String replacement keyword|rawlastnames")), + "n": ("nick", "nickname", + _("String replacement keyword|nickname")), + "g": ("famnick", "famnick", + _("String replacement keyword|famnick")), } - args = "first,raw_surname_list,suffix,title,call" + args = "first,raw_surname_list,suffix,title,call,nick,famnick" return self._make_fn(format_str, d, args) def _make_fn(self, format_str, d, args): @@ -567,7 +581,8 @@ def fn(%s): def format_str(self, name, format_str): return self._format_str_base(name.first_name, name.surname_list, name.suffix, name.title, - name.call, format_str) + name.call, name.nick, name.famnick, + format_str) def format_str_raw(self, raw_data, format_str): """ @@ -587,7 +602,7 @@ def fn(%s): def _format_str_base(self, first, surname_list, suffix, title, call, - format_str): + nick, famnick, format_str): """ Generates name from a format string. @@ -604,6 +619,8 @@ def fn(%s): '%p' : list of all prefixes '%q' : surnames without prefixes and connectors '%s' : suffix + '%n' : nick name + '%g' : family nick name The capital letters are substituted for capitalized name components. The %% is substituted with the single % character. All the other characters in the fmt_str are unaffected. @@ -614,7 +631,7 @@ def fn(%s): self.__class__.format_funcs[format_str] = func try: s = func(first, [surn.serialize() for surn in surname_list], - suffix, title, call) + suffix, title, call, nick, famnick) except (ValueError, TypeError,): raise NameDisplayError, "Incomplete format string" diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index c103c8865..73c0f10ea 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.py @@ -78,7 +78,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, if data: (privacy, source_list, note, date, self.first_name, surname_list, self.suffix, self.title, name_type, - self.group_as, self.sort_as, self.display_as, self.call) = data + self.group_as, self.sort_as, self.display_as, self.call, + self.nick, self.famnick) = data self.type = NameType(name_type) SurnameBase.unserialize(self, surname_list) PrivacyBase.unserialize(self, privacy) @@ -94,6 +95,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, self.sort_as = source.sort_as self.display_as = source.display_as self.call = source.call + self.nick = source.nick + self.famnick = source.famnick else: self.first_name = "" self.suffix = "" @@ -103,6 +106,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, self.sort_as = self.DEF self.display_as = self.DEF self.call = u'' + self.nick = u'' + self.famnick = u'' def serialize(self): """ @@ -116,14 +121,16 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, SurnameBase.serialize(self), self.suffix, self.title, self.type.serialize(), - self.group_as, self.sort_as, self.display_as, self.call) + self.group_as, self.sort_as, self.display_as, self.call, + self.nick, self.famnick) def is_empty(self): """ Indicate if the name is empty. """ namefieldsempty = (self.first_name == u"" and - self.suffix == u"" and self.title == u"") + self.suffix == u"" and self.title == u"" and self.nick ==u"" + and self.famnick == u"") surnamefieldsempty = not (False in [surn.is_empty() for surn in self.surname_list]) return namefieldsempty and surnamefieldsempty @@ -134,7 +141,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, """ (privacy, source_list, note_list, date, self.first_name, surname_list, self.suffix, self.title, name_type, - self.group_as, self.sort_as, self.display_as, self.call) = data + self.group_as, self.sort_as, self.display_as, self.call, + self.nick, self.famnick) = data self.type = NameType(name_type) PrivacyBase.unserialize(self, privacy) SurnameBase.unserialize(self, surname_list) @@ -151,7 +159,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, :rtype: list """ return [self.first_name, self.suffix, self.title, - str(self.type), self.call] + str(self.type), self.call, self.nick, self.famnick] def get_text_data_child_list(self): """ @@ -219,7 +227,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, Normally the person merge code should opt for adding an alternate name if names are actually different (like not equal surname list) - Lost: type, first, call, suffix, title and date of + Lost: type, first, call, suffix, title, nick, famnick and date of acquisition. :param acquisition: The name to merge with the present name. @@ -314,6 +322,42 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, """ self.call = val + def get_nick_name(self): + """ + Return the nick name. + + The nick name of the person, a not official name the person is known + with. + """ + return self.nick + + def set_nick_name(self, val): + """ + Set the nick name. + + The nick name of the person, a not official name the person is known + with. + """ + self.nick = val + + def get_family_nick_name(self): + """ + Return the family nick name. + + The family nick name of the family of the person, a not official name + use to denote the entire family. + """ + return self.famnick + + def set_family_nick_name(self, val): + """ + Set the family nick name. + + The family nick name of the family of the person, a not official name + use to denote the entire family. + """ + self.famnick = val + def set_type(self, the_type): """Set the type of the Name instance.""" self.type.set(the_type) @@ -396,6 +440,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, retval['surnamelist'] = self.get_surnames() retval['prefixes'] = self.get_prefixes() retval['connectors'] = self.get_connectors() + retval['nick'] = self.nick + retval['famnick'] = self.famnick return retval def get_gedcom_name(self): diff --git a/src/plugins/import/ImportXml.py b/src/plugins/import/ImportXml.py index 749dc3c97..5bbce007d 100644 --- a/src/plugins/import/ImportXml.py +++ b/src/plugins/import/ImportXml.py @@ -416,6 +416,7 @@ class GrampsParser(UpdateCallback): self.childref = None self.personref = None self.name = None + self.surname = None self.home = None self.owner = gen.lib.Researcher() self.func_list = [None]*50 diff --git a/src/plugins/lib/libgrampsxml.py b/src/plugins/lib/libgrampsxml.py index 09ca97676..cc2f79869 100644 --- a/src/plugins/lib/libgrampsxml.py +++ b/src/plugins/lib/libgrampsxml.py @@ -35,5 +35,5 @@ # Public Constants # #------------------------------------------------------------------------ -GRAMPS_XML_VERSION = "1.3.0" +GRAMPS_XML_VERSION = "1.4.0" From e9599d762d1163d40cda2460766e2e0b219325a3 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 1 Oct 2010 21:22:25 +0000 Subject: [PATCH 08/28] Working namedisplay Working display section in preferences svn: r15946 --- src/Utils.py | 17 ++-- src/gen/display/name.py | 153 ++++++++++++++-------------- src/gen/lib/surname.py | 3 + src/gui/configure.py | 221 +++++++++++++--------------------------- 4 files changed, 164 insertions(+), 230 deletions(-) diff --git a/src/Utils.py b/src/Utils.py index e2809088a..6474b84c4 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -1095,13 +1095,18 @@ def profile(func, *args): # keyword, code, translated standard, translated upper KEYWORDS = [("title", "t", _("Person|Title"), _("Person|TITLE")), ("given", "f", _("Given"), _("GIVEN")), - ("prefix", "p", _("Prefix"), _("PREFIX")), - ("surname", "l", _("Surname"), _("SURNAME")), + ("surname", "l", _("Surname"), _("SURNAME")), + ("call", "c", _("Name|Call"), _("Name|CALL")), + ("common", "x", _("Name|Common"), _("Name|COMMON")), + ("initials", "i", _("Initials"), _("INITIALS")), ("suffix", "s", _("Suffix"), _("SUFFIX")), - ("patronymic","y", _("Patronymic"),_("PATRONYMIC")), - ("call", "c", _("Call"), _("CALL")), - ("common", "x", _("Common"), _("COMMON")), - ("initials", "i", _("Initials"), _("INITIALS")) + ("rawsurnames", "q", _("Rawsurnames"), _("RAWSURNAMES")), + ("patronymic", "y", _("Patronymic"), _("PATRONYMIC")), + ("notpatronymic", "o", _("Notpatronymic"),_("NOTPATRONYMIC")), + ("primary", "m", _("Primary"), _("PRIMARY")), + ("prefix", "p", _("Prefix"), _("PREFIX")), + ("nickname", "n", _("Nickname"), _("NICKNAME")), + ("familynick", "g", _("Familynick"), _("FAMILYNICK")), ] KEY_TO_TRANS = {} TRANS_TO_KEY = {} diff --git a/src/gen/display/name.py b/src/gen/display/name.py index 9367249bb..087f241c3 100644 --- a/src/gen/display/name.py +++ b/src/gen/display/name.py @@ -31,7 +31,7 @@ Specific symbols for parts of a name are defined: 'c' : callname 'x' : callname if existing, otherwise first first name (common name) 'i' : initials of the first names - 'f' : patronymic surname (father) + 'y' : patronymic surname (father) 'o' : surnames without patronymic 'm' : primary surname (main) 'p' : list of all prefixes @@ -101,7 +101,11 @@ _F_RAWFN = 4 # name format raw function # #------------------------------------------------------------------------- # Because of occurring in an exec(), this couldn't be in a lambda: -def _make_cmp(a, b): return -cmp(a[1], b[1]) +# we sort names first on longest first, then last letter first, this to +# avoid translations of shorter terms which appear in longer ones, eg +# namelast may not be mistaken with name, so namelast must first be +# converted to %k before name is converted. +def _make_cmp(a, b): return -cmp((len(a[1]),a[1]), (len(b[1]), b[1])) #------------------------------------------------------------------------- # @@ -144,7 +148,7 @@ def _raw_primary_surname(raw_surn_data_list): return '' def _raw_patro_surname(raw_surn_data_list): - """method for the 'f' symbol: patronymic surname""" + """method for the 'y' symbol: patronymic surname""" for raw_surn_data in raw_surn_data_list: if raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO: result = "%s %s" % (raw_surn_data[_PREFIX_IN_LIST], @@ -191,11 +195,11 @@ class NameDisplay(object): STANDARD_FORMATS = [ (Name.DEF,_("Default format (defined by Gramps preferences)"),'',_ACT), - (Name.LNFN,_("Surname, Given"),'%p %l, %f %s',_ACT), + (Name.LNFN,_("Surname, Given"),'%l, %f %s',_ACT), (Name.FN,_("Given"),'%f',_ACT), - (Name.FNLN,_("Given Surname"),'%f %p %l %s',_ACT), + (Name.FNLN,_("Given Surname"),'%f %l %s',_ACT), # DEPRECATED FORMATS - (Name.PTFN,_("Patronymic, Given"),'%p %y, %s %f',_INA), + (Name.PTFN,_("Patronymic, Given"),'%y, %s %f',_INA), ] def __init__(self): @@ -352,21 +356,21 @@ class NameDisplay(object): raw_data[_FIRSTNAME], raw_data[_SUFFIX]) - Specific symbols for parts of a name are defined: - 't' : title - 'f' : given (first names) - 'l' : full surname (lastname) - 'c' : callname - 'x' : callname if existing, otherwise first first name (common name) - 'i' : initials of the first names - 'f' : patronymic surname (father) - 'o' : surnames without patronymic - 'm' : primary surname (main) - 'p' : list of all prefixes - 'q' : surnames without prefixes and connectors - 's' : suffix - 'n' : nick name - 'g' : family nick name + Specific symbols for parts of a name are defined (keywords given): + 't' : title = title + 'f' : given = given (first names) + 'l' : surname = full surname (lastname) + 'c' : call = callname + 'x' : common = callname if existing, otherwise first first name (common name) + 'i' : initials = initials of the first names + 'y' : patronymic = patronymic surname (father) + 'o' : notpatronymic = surnames without patronymic + 'm' : primary = primary surname (main) + 'p' : prefix = list of all prefixes + 'q' : rawsurnames = surnames without prefixes and connectors + 's' : suffix = suffix + 'n' : nickname = nick name + 'g' : familynick = family nick name """ @@ -374,39 +378,39 @@ class NameDisplay(object): # called to fill in each format flag. # Dictionary is "code": ("expression", "keyword", "i18n-keyword") d = {"t": ("raw_data[_TITLE]", "title", - _("String replacement keyword Person|title")), + _("Person|title")), "f": ("raw_data[_FIRSTNAME]", "given", - _("String replacement keyword|given")), + _("given")), "l": ("_raw_full_surname(raw_data[_SURNAME_LIST])", "surname", - _("String replacement keyword|surname")), + _("surname")), "s": ("raw_data[_SUFFIX]", "suffix", - _("String replacement keyword|suffix")), + _("suffix")), "c": ("raw_data[_CALL]", "call", - _("String replacement keyword|call")), + _("Name|call")), "x": ("(raw_data[_CALL] or raw_data[_FIRSTNAME].split(' ')[0])", "common", - _("String replacement keyword|common")), + _("Name|common")), "i": ("''.join([word[0] +'.' for word in ('. ' +" + " raw_data[_FIRSTNAME]).split()][1:])", "initials", - _("String replacement keyword|initials")), - "f": ("_raw_patro_surname(raw_data[_SURNAME_LIST])", "patronymic", - _("String replacement keyword|patronymic")), + _("initials")), + "y": ("_raw_patro_surname(raw_data[_SURNAME_LIST])", "patronymic", + _("patronymic")), "o": ("_raw_nonpatro_surname(raw_data[_SURNAME_LIST])", "notpatronymic", - _("String replacement keyword|notpatronymic")), + _("notpatronymic")), "m": ("_raw_primary_surname(raw_data[_SURNAME_LIST])", - "primarysurname", - _("String replacement keyword|primarysurname")), + "primary", + _("Name|primary")), "p": ("_raw_prefix_surname(raw_data[_SURNAME_LIST])", "prefix", - _("String replacement keyword|prefix")), + _("prefix")), "q": ("_raw_single_surname(raw_data[_SURNAME_LIST])", "rawsurnames", - _("String replacement keyword|rawsurnames")), + _("rawsurnames")), "n": ("raw_data[_NICK]", "nickname", - _("String replacement keyword|nickname")), - "g": ("raw_data[_FAMNICK]", "famnick", - _("String replacement keyword|famnick")), + _("nickname")), + "g": ("raw_data[_FAMNICK]", "familynick", + _("familynick")), } args = "raw_data" return self._make_fn(format_str, d, args) @@ -427,55 +431,56 @@ class NameDisplay(object): def fn(first, raw_surname_list, suffix, title, call,): return "%s %s" % (first,suffix) - Specific symbols for parts of a name are defined: - 't' : title - 'f' : given (first names) - 'l' : full surname (lastname) - 'c' : callname - 'x' : callname if existing, otherwise first first name (common name) - 'i' : initials of the first names - 'f' : patronymic surname (father) - 'o' : surnames without patronymic - 'm' : primary surname (main) - 'p' : list of all prefixes - 'q' : surnames without prefixes and connectors - 's' : suffix - 'n' : nick name - 'g' : family nick name + Specific symbols for parts of a name are defined (keywords given): + 't' : title = title + 'f' : given = given (first names) + 'l' : surname = full surname (lastname) + 'c' : call = callname + 'x' : common = callname if existing, otherwise first first name (common name) + 'i' : initials = initials of the first names + 'y' : patronymic = patronymic surname (father) + 'o' : notpatronymic = surnames without patronymic + 'm' : primary = primary surname (main) + 'p' : prefix = list of all prefixes + 'q' : rawsurnames = surnames without prefixes and connectors + 's' : suffix = suffix + 'n' : nickname = nick name + 'g' : familynick = family nick name + """ # we need the names of each of the variables or methods that are # called to fill in each format flag. # Dictionary is "code": ("expression", "keyword", "i18n-keyword") d = {"t": ("title", "title", - _("String replacement keyword Person|title")), + _("Person|title")), "f": ("first", "given", - _("String replacement keyword|given")), + _("given")), "l": ("_raw_full_surname(raw_surname_list)", "surname", - _("String replacement keyword|surname")), + _("surname")), "s": ("suffix", "suffix", - _("String replacement keyword|suffix")), + _("suffix")), "c": ("call", "call", - _("String replacement keyword|call")), + _("Name|call")), "x": ("(call or first.split(' ')[0])", "common", - _("String replacement keyword|common")), + _("Name|common")), "i": ("''.join([word[0] +'.' for word in ('. ' + first).split()][1:])", "initials", - _("String replacement keyword|initials")), - "f": ("_raw_patro_surname(raw_surname_list)", "patronymic", - _("String replacement keyword|patronymic")), - "o": ("_raw_nonpatro_surname(raw_surname_list)", "notpatro", - _("String replacement keyword|notpatro")), + _("initials")), + "y": ("_raw_patro_surname(raw_surname_list)", "patronymic", + _("patronymic")), + "o": ("_raw_nonpatro_surname(raw_surname_list)", "notpatronymic", + _("notpatronymic")), "m": ("_raw_primary_surname(raw_surname_list)", "primary", - _("String replacement keyword name|primary")), + _("Name|primary")), "p": ("_raw_prefix_surname(raw_surname_list)", "prefix", - _("String replacement keyword|prefix")), - "q": ("_raw_single_surname(raw_surname_list)", "rawlastnames", - _("String replacement keyword|rawlastnames")), + _("prefix")), + "q": ("_raw_single_surname(raw_surname_list)", "rawsurnames", + _("rawsurnames")), "n": ("nick", "nickname", - _("String replacement keyword|nickname")), - "g": ("famnick", "famnick", - _("String replacement keyword|famnick")), + _("nickname")), + "g": ("famnick", "familynick", + _("familynick")), } args = "first,raw_surname_list,suffix,title,call,nick,famnick" return self._make_fn(format_str, d, args) @@ -497,7 +502,7 @@ class NameDisplay(object): pass else: d_keys = [(code, _tuple[2]) for code, _tuple in d.iteritems()] - d_keys.sort(_make_cmp) # reverse sort by ikeyword + d_keys.sort(_make_cmp) # reverse on length and by ikeyword for (code, ikeyword) in d_keys: exp, keyword, ikeyword = d[code] #ikeyword = unicode(ikeyword, "utf8") @@ -513,7 +518,7 @@ class NameDisplay(object): pass else: d_keys = [(code, _tuple[1]) for code, _tuple in d.iteritems()] - d_keys.sort(_make_cmp) # reverse sort by keyword + d_keys.sort(_make_cmp) # reverse sort on length and by keyword # if in double quotes, just use % codes for (code, keyword) in d_keys: exp, keyword, ikeyword = d[code] @@ -613,7 +618,7 @@ def fn(%s): '%c' : callname '%x' : callname if existing, otherwise first first name (common name) '%i' : initials of the first names - '%f' : patronymic surname (father) + '%y' : patronymic surname (father) '%o' : surnames without patronymic '%m' : primary surname (main) '%p' : list of all prefixes diff --git a/src/gen/lib/surname.py b/src/gen/lib/surname.py index 221bd44ea..8f5a73d84 100644 --- a/src/gen/lib/surname.py +++ b/src/gen/lib/surname.py @@ -188,5 +188,8 @@ class Surname(SecondaryObject): """Set if this surname is the primary surname.replace Use :class:`~gen.lib.surname.SurnameBase` to set the primary surname via :method:`~gen.lib.surname.SurnameBase.set_primary_surname` + + :param primary: primay surname or not + :type primary: bool """ self.primary = primary diff --git a/src/gui/configure.py b/src/gui/configure.py index 87fffa84a..275d93179 100644 --- a/src/gui/configure.py +++ b/src/gui/configure.py @@ -53,7 +53,7 @@ from gen.display.name import displayer as _nd from gen.display.name import NameDisplayError import Utils import gen.lib -from gen.lib import Name +from gen.lib import Name, Surname, NameOriginType import ManagedWindow from gui.widgets import MarkupLabel, BasicLabel from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog @@ -98,21 +98,28 @@ class DisplayNameEditor(ManagedWindow.ManagedWindow): table = self.dialog._build_custom_name_ui() label = gtk.Label(_("""The following keywords will be replaced with the name: - Given - given name (first name) - Surname - surname (last name) - Title - title (Dr., Mrs.) - Prefix - prefix (von, de, de la) - Suffix - suffix (Jr., Sr.) - Call - call name, or nickname - Common - call name, otherwise first part of Given - Patronymic - patronymic (father's name) - Initials - persons's first letters of given names + Given - given name (first name) | Surname - surnames (with prefix and connectors) + Title - title (Dr., Mrs.) | Suffix - suffix (Jr., Sr.) + Call - call name | Nickname - nick name + Initials - first letters of Given | Common - Call, otherwise first of Given + Primary - primary surname (main) | Familynick - Family nick name + Also: + Patronymic - patronymic surname (father's name) + Notpatronymic - all surnames except patronymic + Prefix - all surnames prefixes (von, de, de la) + Rawsurnames - all surnames without prefixes and connectors Use the same keyword in UPPERCASE to force to upper. Parentheses and commas -will be removed around empty fields. Other text will appear literally.""")) +will be removed around empty fields. Other text will appear literally. + +Example fictituous name: 'Dr. Edwin Jose von der Smith and Weston Wilson Sr ("Ed") - Underhills' +Here Edwin Jose are given names, Smith and Weston surnames, Wilson patronymic surname, +Dr. a title, Sr a suffix, Ed the nick name, Underhills family nick name. +Callname is Jose. +""")) label.set_use_markup(True) - self.window.vbox.add(label) - self.window.vbox.add(table) + self.window.vbox.pack_start(label, expand=False) + self.window.vbox.pack_start(table) self.window.set_default_size(600, 550) self.window.connect('response', self.close) self.show() @@ -520,7 +527,7 @@ class GrampsPreferences(ConfigureDialog): gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - index = 0 + index = 0 the_index = 0 for num, name, fmt_str, act in _nd.get_name_format(): translation = fmt_str @@ -538,40 +545,49 @@ class GrampsPreferences(ConfigureDialog): lyst = ["%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"), _("Common")), "%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"), - _("Call")), + _("Nickname")), + "%s, %s %s (%s)" % (_("Surname"), _("Name|Common"), _("Suffix"), + _("Nickname")), + "%s, %s %s" % (_("Surname"), _("Name|Common"), _("Suffix")), "%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"), _("Call")), - "%s, %s (%s)" % (_("Surname"), _("Given"), _("Common")), - "%s, %s (%s)" % (_("Surname"), _("Given"), _("Call")), + "%s, %s (%s)" % (_("Surname"), _("Given"), _("Name|Common")), + "%s, %s (%s)" % (_("Surname"), _("Name|Common"), _("Nickname")), "%s %s" % (_("Given"), _("Surname")), "%s %s, %s" % (_("Given"), _("Surname"), _("Suffix")), - "%s %s %s" % (_("Given"), _("Surname"), _("Patronymic")), + "%s %s %s" % (_("Given"), _("NotPatronymic"), _("Patronymic")), "%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"), _("Common")), - "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Common")), - "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Call")), + "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Name|Common")), + "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Nickname")), "%s %s" % (_("Given"), _("SURNAME")), "%s %s, %s" % (_("Given"), _("SURNAME"), _("Suffix")), "%s /%s/" % (_("Given"), _("SURNAME")), + "%s %s, %s" % (_("Given"), _("Rawsurnames"), _("Suffix")), ] - fmtlyst = ["%s, %s %s (%s)" % ("Surname", "Given", "Suffix", - "Common"), - "%s, %s %s (%s)" % ("Surname", "Given", "Suffix", - "Call"), - "%s, %s %s (%s)" % ("SURNAME", "Given", "Suffix", - "Call"), - "%s, %s (%s)" % ("Surname", "Given", "Common"), - "%s, %s (%s)" % ("Surname", "Given", "Call"), - "%s %s" % ("Given", "Surname"), - "%s %s, %s" % ("Given", "Surname", "Suffix"), - "%s %s %s" % ("Given", "Surname", "Patronymic"), - "%s, %s %s (%s)" % ("SURNAME", "Given", "Suffix", - "Common"), - "%s, %s (%s)" % ("SURNAME", "Given", "Common"), - "%s, %s (%s)" % ("SURNAME", "Given", "Call"), - "%s %s" % ("Given", "SURNAME"), - "%s %s, %s" % ("Given", "SURNAME", "Suffix"), - "%s /%s/" % ("Given", "SURNAME"), + #repeat above list, but not translated. + fmtlyst = ["%s, %s %s (%s)" % (("Surname"), ("Given"), ("Suffix"), + ("Common")), + "%s, %s %s (%s)" % (("Surname"), ("Given"), ("Suffix"), + ("Nickname")), + "%s, %s %s (%s)" % (("Surname"), ("Name|Common"), ("Suffix"), + ("Nickname")), + "%s, %s %s" % (("Surname"), ("Name|Common"), ("Suffix")), + "%s, %s %s (%s)" % (("SURNAME"), ("Given"), ("Suffix"), + ("Call")), + "%s, %s (%s)" % (("Surname"), ("Given"), ("Name|Common")), + "%s, %s (%s)" % (("Surname"), ("Name|Common"), ("Nickname")), + "%s %s" % (("Given"), ("Surname")), + "%s %s, %s" % (("Given"), ("Surname"), ("Suffix")), + "%s %s %s" % (("Given"), ("NotPatronymic"), ("Patronymic")), + "%s, %s %s (%s)" % (("SURNAME"), ("Given"), ("Suffix"), + ("Common")), + "%s, %s (%s)" % (("SURNAME"), ("Given"), ("Name|Common")), + "%s, %s (%s)" % (("SURNAME"), ("Given"), ("Nickname")), + "%s %s" % (("Given"), ("SURNAME")), + "%s %s, %s" % (("Given"), ("SURNAME"), ("Suffix")), + "%s /%s/" % (("Given"), ("SURNAME")), + "%s %s, %s" % (("Given"), ("Rawsurnames"), ("Suffix")), ] rand = int(random.random() * len(lyst)) f = lyst[rand] @@ -722,11 +738,9 @@ class GrampsPreferences(ConfigureDialog): self.insert_button = gtk.Button(stock=gtk.STOCK_ADD) self.insert_button.connect('clicked', self.__new_name) - #self.cb_insert_fmt_str) self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT) self.edit_button.connect('clicked', self.__edit_name) - #self.cb_edit_fmt_str) self.edit_button.set_sensitive(False) self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE) @@ -781,44 +795,6 @@ class GrampsPreferences(ConfigureDialog): self.edit_button.set_sensitive(idx) self.name_renderer.set_property('editable', idx) - - def cb_edit_fmt_str(self, obj): - """ - Name format editor Edit button callback - """ - num, name, fmt = self.selected_fmt[COL_NUM:COL_EXPL] - dlg = NameFormatEditDlg(name, fmt, self.examplename) - dlg.dlg.set_transient_for(self.window) - (res, name, fmt) = dlg.run() - - if res == gtk.RESPONSE_OK and (name != self.selected_fmt[COL_NAME] or - fmt != self.selected_fmt[COL_FMT]): - exmpl = _nd.format_str(self.examplename, fmt) - self.fmt_model.set(self.iter, COL_NAME, name, - COL_FMT, fmt, - COL_EXPL, exmpl) - self.selected_fmt = (num, name, fmt, exmpl) - _nd.edit_name_format(num, name, fmt) - - self.dbstate.db.name_formats = _nd.get_name_format(only_custom=True, - only_active=False) - - def cb_insert_fmt_str(self, obj): - """ - Name format editor Insert button callback - """ - dlg = NameFormatEditDlg('', '', self.examplename) - dlg.dlg.set_transient_for(self.window) - (res, n, f) = dlg.run() - - if res == gtk.RESPONSE_OK: - i = _nd.add_name_format(n, f) - self.fmt_model.append(row=[i, n, f, - _nd.format_str(self.examplename, f)]) - - self.dbstate.db.name_formats = _nd.get_name_format(only_custom=True, - only_active=False) - def cb_del_fmt_str(self, obj): """ Name format editor Remove button callback @@ -842,13 +818,26 @@ class GrampsPreferences(ConfigureDialog): # Display name: self.examplename = Name() + examplesurname = Surname() + examplesurnamesecond = Surname() + examplesurnamepat = Surname() self.examplename.set_title('Dr.') self.examplename.set_first_name('Edwin Jose') - self.examplename.set_surname_prefix('von der') - self.examplename.set_surname('Smith') + examplesurname.set_prefix('von der') + examplesurname.set_surname('Smith') + examplesurname.set_connector('and') + self.examplename.add_surname(examplesurname) + examplesurnamesecond.set_surname('Weston') + self.examplename.add_surname(examplesurnamesecond) + examplesurnamepat.set_surname('Wilson') + examplesurnamepat.set_origintype( + NameOriginType(NameOriginType.PATRONYMIC)) + self.examplename.add_surname(examplesurnamepat) + self.examplename.set_primary_surname(0) self.examplename.set_suffix('Sr') - self.examplename.set_patronymic('Wilson') - self.examplename.set_call_name('Ed') + self.examplename.set_call_name('Jose') + self.examplename.set_nick_name('Ed') + self.examplename.set_family_nick_name('Underhills') # get the model for the combo and the treeview active = _nd.get_default_format() self.fmt_model, active = self._build_name_format_model(active) @@ -1186,71 +1175,3 @@ class GrampsPreferences(ConfigureDialog): button.add(image) button.show() return button - -class NameFormatEditDlg(object): - """ - """ - - def __init__(self, fmt_name, fmt_str, name): - self.fmt_name = fmt_name - self.fmt_str = fmt_str - self.name = name - self.valid = True - self.top = Glade() - - self.dlg = self.top.get_object('namefmt_edit') - ManagedWindow.set_titles(self.dlg, None, _('Name Format Editor')) - - self.examplelabel = self.top.get_object('example_label') - - self.nameentry = self.top.get_object('name_entry') - self.nameentry.set_text('%s' % self.fmt_name) - self.nameentry.set_use_markup(True) - - self.formatentry = self.top.get_object('format_entry') - self.formatentry.connect('changed', self.cb_format_changed) - self.formatentry.set_text(self.fmt_str) - - def run(self): - running = True - while running: - self.response = self.dlg.run() - - running = False - self.fmt_name = self.nameentry.get_text() - self.fmt_str = self.formatentry.get_text() - - if self.response == gtk.RESPONSE_OK: - if not self.valid: - q = QuestionDialog2( - _('The format definition is invalid'), - _('What would you like to do?'), - _('_Continue anyway'), _('_Modify format'), - parent=self.dlg) - running = not q.run() - self.response = gtk.RESPONSE_CANCEL - elif self.fmt_name == '' and self.fmt_str == '': - self.response = gtk.RESPONSE_CANCEL - elif (self.fmt_name == '') ^ (self.fmt_str == ''): - ErrorDialog( - _('Both Format name and definition have to be defined.'), - parent=self.dlg) - running = True - - self.dlg.destroy() - return (self.response, self.fmt_name, self.fmt_str) - - def cb_format_changed(self, obj): - try: - t = (_nd.format_str(self.name, escape(obj.get_text()))) - sample = '%s' % t - self.valid = True - except NameDisplayError: - t = _("Invalid or incomplete format definition.") - sample = '%s' % t - self.valid = False - - self.examplelabel.set_text(sample) - self.examplelabel.set_use_markup(True) - self.nameentry.set_text('%s' % obj.get_text()) - self.nameentry.set_use_markup(True) From 043d35a354ca21fa244e04d433de5a315b5ba678 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 2 Oct 2010 12:52:14 +0000 Subject: [PATCH 09/28] Working XML import with new surnames svn: r15947 --- data/grampsxml.dtd | 23 ++-- data/grampsxml.rng | 33 +++-- src/plugins/import/ImportXml.py | 220 +++++++++++++++++++++++--------- 3 files changed, 195 insertions(+), 81 deletions(-) diff --git a/data/grampsxml.dtd b/data/grampsxml.dtd index b55ef1350..9a741801c 100644 --- a/data/grampsxml.dtd +++ b/data/grampsxml.dtd @@ -55,7 +55,7 @@ DATABASE - + - - - - + + + - + + @@ -150,7 +150,6 @@ - @@ -221,13 +220,13 @@ - - - - - + + + + + + - @@ -238,6 +237,24 @@ + + + + + + 0 + 1 + + + inherited + patronymic + matronymic + other + + + + + diff --git a/src/plugins/import/ImportXml.py b/src/plugins/import/ImportXml.py index 5bbce007d..8ece4a010 100644 --- a/src/plugins/import/ImportXml.py +++ b/src/plugins/import/ImportXml.py @@ -94,9 +94,23 @@ def importData(database, filename, callback=None, cl=0): database.smap = {} database.pmap = {} database.fmap = {} + + xml_file = open_file(filename, cl) + versionparser = VersionParser(xml_file) + if xml_file is None or \ + version_is_valid(versionparser, cl) is False: + if cl: + sys.exit(1) + else: + return + + version_string = versionparser.get_xmlns_version() + #reset file to the start + xml_file.seek(0) + change = os.path.getmtime(filename) - parser = GrampsParser(database, callback, change) + parser = GrampsParser(database, callback, change, version_string) linecounter = LineParser(filename) line_cnt = linecounter.get_count() @@ -105,17 +119,7 @@ def importData(database, filename, callback=None, cl=0): read_only = database.readonly database.readonly = False - xml_file = open_file(filename, cl) - - if xml_file is None or \ - version_is_valid(xml_file, cl) is False: - if cl: - sys.exit(1) - else: - return - try: - xml_file.seek(0) info = parser.parse(xml_file, line_cnt, person_cnt) except IOError, msg: if cl: @@ -342,8 +346,10 @@ class LineParser(object): #------------------------------------------------------------------------- class GrampsParser(UpdateCallback): - def __init__(self, database, callback, change): + def __init__(self, database, callback, change, version_string): UpdateCallback.__init__(self, callback) + #version of the xml file + self.version_string = version_string self.stext_list = [] self.scomments_list = [] self.note_list = [] @@ -417,6 +423,7 @@ class GrampsParser(UpdateCallback): self.personref = None self.name = None self.surname = None + self.surnamepat = None self.home = None self.owner = gen.lib.Researcher() self.func_list = [None]*50 @@ -434,17 +441,33 @@ class GrampsParser(UpdateCallback): self.eidswap = {} self.func_map = { + #name part + "name": (self.start_name, self.stop_name), + "first": (None, self.stop_first), + "call": (None, self.stop_call), + "aka": (self.start_name, self.stop_aka), #deprecated < 1.3.0 + "last": (self.start_last, self.stop_last), #deprecated in 1.4.0 + "nick": (None, self.stop_nick), + "title": (None, self.stop_title), + "suffix": (None, self.stop_suffix), + "patronymic": (self.start_patronymic, self.stop_patronymic), #deprecated in 1.4.0 + "familynick": (None, self.stop_familynick), #new in 1.4.0 + "group": (None, self.stop_group), #new in 1.4.0, replaces attribute + #new in 1.4.0 + "surname": (self.start_surname, self.stop_surname), + # + "namemaps": (None, None), + "name-formats": (None, None), + #other "address": (self.start_address, self.stop_address), "addresses": (None, None), - "childlist": (None, None), - "aka": (self.start_name, self.stop_aka), + "childlist": (None, None), "attribute": (self.start_attribute, self.stop_attribute), "attr_type": (None, self.stop_attr_type), "attr_value": (None, self.stop_attr_value), "bookmark": (self.start_bmark, None), "bookmarks": (None, None), "format": (self.start_format, None), - "name-formats": (None, None), "child": (self.start_child, None), "childof": (self.start_childof, None), "childref": (self.start_childref, self.stop_childref), @@ -470,17 +493,11 @@ class GrampsParser(UpdateCallback): "rel": (self.start_rel, None), "region": (self.start_region, None), "father": (self.start_father, None), - "first": (None, self.stop_first), - "call": (None, self.stop_call), "gender": (None, self.stop_gender), "header": (None, None), - "last": (self.start_last, self.stop_last), "map": (self.start_namemap, None), "mediapath": (None, self.stop_mediapath), "mother": (self.start_mother, None), - "name": (self.start_name, self.stop_name), - "namemaps": (None, None), - "nick": (None, self.stop_nick), "note": (self.start_note, self.stop_note), "noteref": (self.start_noteref, None), "p": (None, self.stop_ptag), @@ -505,7 +522,6 @@ class GrampsParser(UpdateCallback): "status": (self.start_status, None), "sealed_to": (self.start_sealed_to, None), "coord": (self.start_coord, None), - "patronymic": (None, self.stop_patronymic), "pos": (self.start_pos, None), "postal": (None, self.stop_postal), "range": (self.start_range, None), @@ -530,10 +546,8 @@ class GrampsParser(UpdateCallback): "stext": (None, self.stop_stext), "stitle": (None, self.stop_stitle), "street": (None, self.stop_street), - "suffix": (None, self.stop_suffix), "tag": (self.start_tag, None), "text": (None, self.stop_text), - "title": (None, self.stop_title), "url": (self.start_url, None), "repository": (self.start_repo, self.stop_repo), "reporef": (self.start_reporef, self.stop_reporef), @@ -734,6 +748,11 @@ class GrampsParser(UpdateCallback): return self.nidswap[gramps_id] def parse(self, ifile, linecount=0, personcount=0): + """ + Parse the xml file + :param ifile: must be a file handle that is already open, with position + at the start of the file + """ if personcount < 1000: no_magic = True else: @@ -1303,6 +1322,14 @@ class GrampsParser(UpdateCallback): except KeyError: pass + def start_surname(self, attrs): + self.surname = gen.lib.Surname() + self.surname.set_prefix(attrs.get("prefix", "")) + self.surname.set_primary(bool(attrs.get("primary",0))) + self.surname.set_connector(attrs.get("connector", "")) + origin_type = attrs.get("derivation", "") + self.surname.origintype.set_from_xml_str(origin_type) + def start_namemap(self, attrs): type = attrs.get('type') key = attrs['key'] @@ -1319,9 +1346,17 @@ class GrampsParser(UpdateCallback): self.db.set_name_group_mapping(key, value) def start_last(self, attrs): - self.name.prefix = attrs.get('prefix', '') + """ This is the element in version < 1.4.0 to do the surname""" + self.surname = gen.lib.Surname() + self.surname.prefix = attrs.get('prefix', '') self.name.group_as = attrs.get('group', '') - + + def start_patronymic(self, attrs): + """ This is the element in version < 1.4.0 to do the patronymic""" + self.surnamepat = gen.lib.Surname() + self.surnamepat.set_origintype(gen.lib.NameTypeOrigin( + gen.lib.NameTypeOrigin.PATRONYMIC)) + def start_tag(self, attrs): tagtype = gen.lib.StyledTextTagType() tagtype.set_from_xml_str(attrs['name']) @@ -1889,14 +1924,8 @@ class GrampsParser(UpdateCallback): self.num_places = 0 def start_database(self, attrs): - try: - # This is a proper way to get the XML version - xmlns = attrs.get('xmlns') - self.version_string = xmlns.split('/')[4] - except: - # Before we had a proper DTD, the version was hard to determine - # so we're setting it to 1.0.0 - self.version_string = '1.0.0' + # we already parsed xml once in VersionParser to obtain version + pass def start_pos(self, attrs): self.person.position = (int(attrs["x"]), int(attrs["y"])) @@ -2040,15 +2069,62 @@ class GrampsParser(UpdateCallback): self.db.commit_note(note, self.trans, self.change) self.info.add('new-object', NOTE_KEY, note) self.event.add_note(note.handle) - elif self.alt_name: - # former aka tag -- alternate name - if self.name.get_type() == "": - self.name.set_type(gen.lib.NameType.AKA) - self.person.add_alternate_name(self.name) + else: + #first correct old xml that has no nametype set + if self.alt_name: + # alternate name or former aka tag + if self.name.get_type() == "": + self.name.set_type(gen.lib.NameType.AKA) + else: + if self.name.get_type() == "": + self.name.set_type(gen.lib.NameType.BIRTH) + + #same logic as bsddb upgrade for xml < 1.4.0 which will + #have a surnamepat and/or surname. From 1.4.0 surname has been + #added to name in self.stop_surname + if not self.surnamepat: + #no patronymic, only add surname if present + if self.surname: + self.name.add_surname(self.surname) + self.name.set_primary_surname(0) + else: + #a patronymic, if no surname, a single surname + if not self.surname: + self.name.add_surname(self.surnamepat) + self.name.set_primary_surname(0) + else: + #two surnames, first patronymic, then surname which is primary + self.name.add_surname(self.surnamepat) + self.name.add_surname(self.surname) + self.name.set_primary_surname(1) + if self.alt_name: + self.person.add_alternate_name(self.name) + else: + self.person.set_primary_name(self.name) + self.name = None + self.surname = None + self.surnamepat = None + + + def stop_aka(self, tag): + if self.name.get_type() == "": + self.name.set_type(gen.lib.NameType.AKA) + if not self.surnamepat: + #no patronymic, only add surname if present + if self.surname: + self.name.add_surname(self.surname) + self.name.set_primary_surname(0) else: - if self.name.get_type() == "": - self.name.set_type(gen.lib.NameType.BIRTH) - self.person.set_primary_name (self.name) + #a patronymic, if no surname, a single surname + if not self.surname: + self.name.add_surname(self.surnamepat) + self.name.set_primary_surname(0) + else: + #two surnames, first patronymic, then surname which is primary + self.name.add_surname(self.surnamepat) + self.name.add_surname(self.surname) + self.name.set_primary_surname(1) + self.person.add_alternate_name(self.name) self.name = None def stop_rname(self, tag): @@ -2218,28 +2294,53 @@ class GrampsParser(UpdateCallback): self.source_ref.add_note(note.handle) def stop_last(self, tag): + if self.surname: + self.surname.set_surname(tag) + if not tag.strip() and not self.surname.get_prefix().strip(): + #consider empty surname as no surname + self.surname = None + + def stop_surname(self, tag): if self.name: - self.name.set_surname(tag) + self.surname.set_surname(tag) + self.name.add_surname(self.surname) + self.surname = None + + def stop_group(self, tag): + """ group name of a name""" + if self.name: + self.name.set_group_as(tag) def stop_suffix(self, tag): if self.name: self.name.set_suffix(tag) def stop_patronymic(self, tag): - if self.name: - self.name.set_patronymic(tag) + if self.surnamepat: + self.surnamepat.set_surname(tag) + if not tag.strip(): + self.surnamepat = None def stop_title(self, tag): if self.name: self.name.set_title(tag) def stop_nick(self, tag): - if self.person: + """in < 1.3.0 nick is on person and mapped to attribute + from 1.4.0 it is a name element + """ + if self.name: + self.name.set_nick_name(tag) + elif self.person: attr = gen.lib.Attribute() attr.set_type(gen.lib.AttributeType.NICKNAME) attr.set_value(tag) self.person.add_attribute(attr) + def stop_familynick(self, tag): + if self.name: + self.name.set_family_nick_name(tag) + def stop_text(self, tag): self.note_text = tag @@ -2341,12 +2442,6 @@ class GrampsParser(UpdateCallback): elif self.in_scomments: self.scomments_list.append(tag) - def stop_aka(self, tag): - self.person.add_alternate_name(self.name) - if self.name.get_type() == "": - self.name.set_type(gen.lib.NameType.AKA) - self.name = None - def startElement(self, tag, attrs): self.func_list[self.func_index] = (self.func, self.tlist) self.func_index += 1 @@ -2409,7 +2504,6 @@ class VersionParser(object): self.__p.StartElementHandler = self.__element_handler self.__gramps_version = 'unknown' self.__xml_version = '1.0.0' - xml_file.seek(0) self.__p.ParseFile(xml_file) @@ -2481,25 +2575,25 @@ def open_file(filename, cli): return xml_file -def version_is_valid(filename, cli): +def version_is_valid(versionparser, cli): """ Validate the xml version. + :param versionparser: A VersionParser object to work with """ - parser = VersionParser(filename) - if parser.get_xmlns_version() > libgrampsxml.GRAMPS_XML_VERSION: + if versionparser.get_xmlns_version() > libgrampsxml.GRAMPS_XML_VERSION: msg = _("The .gramps file you are importing was made by version %(newer)s of " "Gramps, while you are running an older version %(older)s. " "The file will not be imported. Please upgrade to the latest " "version of Gramps and try again." ) % { - 'newer' : parser.get_gramps_version(), 'older' : const.VERSION } + 'newer' : versionparser.get_gramps_version(), 'older' : const.VERSION } if cli: LOG.warn(msg) return False else: ErrorDialog(msg) return False - if parser.get_xmlns_version() < '1.0.0': + if versionparser.get_xmlns_version() < '1.0.0': msg = _("The .gramps file you are importing was made by version " "%(oldgramps)s of Gramps, while you are running a more " "recent version %(newgramps)s.\n\n" @@ -2507,9 +2601,9 @@ def version_is_valid(filename, cli): " Gramps that supports version %(xmlversion)s of the xml.\nSee" "\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n " "for more info." - ) % {'oldgramps': parser.get_gramps_version(), + ) % {'oldgramps': versionparser.get_gramps_version(), 'newgramps': const.VERSION, - 'xmlversion': parser.get_xmlns_version(), + 'xmlversion': versionparser.get_xmlns_version(), } if cli: LOG.warn(msg) @@ -2517,7 +2611,7 @@ def version_is_valid(filename, cli): else: ErrorDialog(_('The file will not be imported'), msg) return False - elif parser.get_xmlns_version() < '1.1.0': + elif versionparser.get_xmlns_version() < '1.1.0': msg = _("The .gramps file you are importing was made by version " "%(oldgramps)s of Gramps, while you are running a much " "more recent version %(newgramps)s.\n\n" @@ -2527,9 +2621,9 @@ def version_is_valid(filename, cli): "is version %(xmlversion)s of the xml.\nSee" "\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n" "for more info." - ) % {'oldgramps': parser.get_gramps_version(), + ) % {'oldgramps': versionparser.get_gramps_version(), 'newgramps': const.VERSION, - 'xmlversion': parser.get_xmlns_version(), + 'xmlversion': versionparser.get_xmlns_version(), } if cli: LOG.warn(msg) From 0ca48dd81107628b67fc65d8c5891bbc8f3e205d Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 2 Oct 2010 13:38:36 +0000 Subject: [PATCH 10/28] Working xml export svn: r15948 --- data/grampsxml.dtd | 2 +- data/grampsxml.rng | 2 +- src/gen/db/read.py | 7 ++++- src/gen/lib/surname.py | 3 ++- src/plugins/export/ExportXml.py | 45 +++++++++++++++++++++------------ 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/data/grampsxml.dtd b/data/grampsxml.dtd index 9a741801c..fe5e35ea5 100644 --- a/data/grampsxml.dtd +++ b/data/grampsxml.dtd @@ -133,7 +133,7 @@ GENDER has values of M, F, or U. diff --git a/data/grampsxml.rng b/data/grampsxml.rng index b73b655a5..6b9ffc72e 100644 --- a/data/grampsxml.rng +++ b/data/grampsxml.rng @@ -242,8 +242,8 @@ - 0 1 + 0 inherited diff --git a/src/gen/db/read.py b/src/gen/db/read.py index e9ffb141c..64ccd6e7f 100644 --- a/src/gen/db/read.py +++ b/src/gen/db/read.py @@ -1356,7 +1356,12 @@ class DbBsddbRead(DbReadBase, Callback): return self.__has_handle(self.source_map, handle) def __sortbyperson_key(self, person): - return locale.strxfrm(self.person_map.get(str(person))[3][5]) + surnlist = self.person_map.get(str(person))[3][5] + if surnlist: + surn = " ".join([x[0] for x in surnlist]) + else: + surn = "" + return locale.strxfrm(surn) def __sortbyplace(self, first, second): return locale.strcoll(self.place_map.get(str(first))[2], diff --git a/src/gen/lib/surname.py b/src/gen/lib/surname.py index 8f5a73d84..251f308b5 100644 --- a/src/gen/lib/surname.py +++ b/src/gen/lib/surname.py @@ -47,6 +47,7 @@ class Surname(SecondaryObject): def __init__(self, source=None, data=None): """Create a new Surname instance, copying from the source if provided. + By default a surname is created as primary, use set_primary to change """ if source: self.surname = source.surname @@ -57,7 +58,7 @@ class Surname(SecondaryObject): else: self.surname = "" self.prefix = "" - self.primary = False + self.primary = True self.origintype = NameOriginType() self.connector = "" if data: diff --git a/src/plugins/export/ExportXml.py b/src/plugins/export/ExportXml.py index f7ffee567..57a200e1a 100644 --- a/src/plugins/export/ExportXml.py +++ b/src/plugins/export/ExportXml.py @@ -738,16 +738,26 @@ class GrampsXmlWriter(UpdateCallback): if rel != "": self.g.write(' %s\n' % (sp,rel) ) - def write_last(self, name,indent=1): - p = name.get_surname_prefix() - n = name.get_surname() - g = name.get_group_as() - self.g.write('%s%s\n' % self.fix(n)) + def write_surname(self, surname, indent=1): + """ + Writes a surname of the name + """ + pre = surname.get_prefix() + con = surname.get_connector() + nam = surname.get_surname() + der = surname.get_origintype().xml_str() + pri = surname.get_primary() + self.g.write('%s%s\n' % self.fix(nam)) def write_line(self,tagname,value,indent=1): if value: @@ -870,12 +880,15 @@ class GrampsXmlWriter(UpdateCallback): if name.get_display_as() != 0: self.g.write(' display="%d"' % name.get_display_as()) self.g.write('>\n') - self.write_line("first", name.get_first_name(),index+1) - self.write_line("call", name.get_call_name(),index+1) - self.write_last(name,index+1) - self.write_line("suffix", name.get_suffix(),index+1) - self.write_line("patronymic", name.get_patronymic(),index+1) - self.write_line("title", name.get_title(),index+1) + self.write_line("first", escxml(name.get_first_name()),index+1) + self.write_line("call", escxml(name.get_call_name()),index+1) + for surname in name.get_surname_list(): + self.write_surname(surname,index+1) + self.write_line("suffix", escxml(name.get_suffix()),index+1) + self.write_line("title", escxml(name.get_title()),index+1) + self.write_line("nick", escxml(name.get_nick_name()), index+1) + self.write_line("familynick", escxml(name.get_family_nick_name()), index+1) + self.write_line("group", escxml(name.get_group_as()), index+1) if name.date: self.write_date(name.date,4) self.write_note_list(name.get_note_list(),index+1) From 371205ec700af64fe2829002b10b3624ba42b0b5 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 2 Oct 2010 16:01:34 +0000 Subject: [PATCH 11/28] Fix surname index for autocompletion Fix check tool and start with importgrdb upgrade svn: r15949 --- src/gen/db/read.py | 45 +++++++++-- src/gen/db/upgrade.py | 14 +++- src/gen/db/write.py | 21 +++--- src/plugins/import/ImportGrdb.py | 124 +++++++++++++++++++++++++++---- src/plugins/tool/Check.py | 2 + 5 files changed, 172 insertions(+), 34 deletions(-) diff --git a/src/gen/db/read.py b/src/gen/db/read.py index 64ccd6e7f..63db74f70 100644 --- a/src/gen/db/read.py +++ b/src/gen/db/read.py @@ -46,7 +46,7 @@ import logging # #------------------------------------------------------------------------- from gen.lib import (MediaObject, Person, Family, Source, Event, Place, - Repository, Note, GenderStats, Researcher) + Repository, Note, GenderStats, Researcher, NameOriginType) from gen.db.dbconst import * from gen.utils.callback import Callback from gen.db import (BsddbBaseCursor, DbReadBase) @@ -67,6 +67,41 @@ _SIGBASE = ('person', 'family', 'source', 'event', DBERRS = (db.DBRunRecoveryError, db.DBAccessError, db.DBPageNotFoundError, db.DBInvalidArgError) +#------------------------------------------------------------------------- +# +# Helper functions +# +#------------------------------------------------------------------------- +def find_surname(key, data): + """ + Creating a surname from raw data of a person, to use for sort and index + """ + return __index_surname(data[3][5]) + +def find_surname_name(key, data): + """ + Creating a surname from raw name, to use for sort and index + """ + return __index_surname(data[5]) + +def __index_surname(surn_list): + """ + All non pa/matronymic surnames are used in indexing. + pa/matronymic not as they change for every generation! + """ + if surn_list: + surn = " ".join([x[0] for x in surn_list if not (x[3][0] in [ + NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]) ]) + else: + surn = "" + return str(surn) + + +#------------------------------------------------------------------------- +# +# class DbBookmarks +# +#------------------------------------------------------------------------- class DbBookmarks(object): def __init__(self, default=[]): self.bookmarks = list(default) # want a copy (not an alias) @@ -1356,12 +1391,8 @@ class DbBsddbRead(DbReadBase, Callback): return self.__has_handle(self.source_map, handle) def __sortbyperson_key(self, person): - surnlist = self.person_map.get(str(person))[3][5] - if surnlist: - surn = " ".join([x[0] for x in surnlist]) - else: - surn = "" - return locale.strxfrm(surn) + return locale.strxfrm(find_surname(str(person), + self.person_map.get(str(person)))) def __sortbyplace(self, first, second): return locale.strcoll(self.place_map.get(str(first))[2], diff --git a/src/gen/db/upgrade.py b/src/gen/db/upgrade.py index be383ad2e..fbae5746e 100644 --- a/src/gen/db/upgrade.py +++ b/src/gen/db/upgrade.py @@ -25,17 +25,17 @@ from __future__ import with_statement """ methods to upgrade a database from version 13 to current version """ - +from bsddb import db from gen.db import BSDDBTxn from gen.lib.nameorigintype import NameOriginType - +from gen.db.write import _mkname, SURNAMES def gramps_upgrade_15(self): """Upgrade database from version 14 to 15. This upgrade adds: * tagging * surname list """ - length = len(self.person_map) + length = len(self.person_map)+10 self.set_total(length) # --------------------------------- @@ -94,7 +94,13 @@ def gramps_upgrade_15(self): ) with BSDDBTxn(self.env, self.person_map) as txn: txn.put(str(handle), new_person) - self.update() + self.update(length) + #surname is now different, remove secondary index with names + _db = db.DB(self.env) + try: + _db.remove(_mkname(self.full_name, SURNAMES), SURNAMES) + except db.DBNoSuchFileError: + pass # Bump up database version. Separate transaction to save metadata. with BSDDBTxn(self.env, self.metadata) as txn: diff --git a/src/gen/db/write.py b/src/gen/db/write.py index 294496f5d..8966498fe 100644 --- a/src/gen/db/write.py +++ b/src/gen/db/write.py @@ -52,7 +52,7 @@ from gen.lib import (GenderStats, Person, Family, Event, Place, Source, MediaObject, Repository, Note) from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn, DbTxn, BsddbBaseCursor, DbVersionError, - DbUpgradeRequiredError, + DbUpgradeRequiredError, find_surname, find_surname_name, DbUndoBSDDB as DbUndo) from gen.db.dbconst import * from gen.utils.callback import Callback @@ -120,10 +120,7 @@ KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__, # # Helper functions # -#------------------------------------------------------------------------- - -def find_surname(key, data): - return str(data[3][5]) +#------------------------------------------------------------------------- def find_idmap(key, data): return str(data[1]) @@ -1315,7 +1312,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): """ Build surname list for use in autocompletion """ - self.surname_list = sorted(map(unicode, set(self.surnames.keys())), key=locale.strxfrm) + self.surname_list = sorted(map(unicode, set(self.surnames.keys())), + key=locale.strxfrm) def add_to_surname_list(self, person, batch_transaction): """ @@ -1323,7 +1321,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): """ if batch_transaction: return - name = unicode(person.get_primary_name().get_surname()) + name = unicode(find_surname_name(person.handle, + person.get_primary_name().serialize())) i = bisect.bisect(self.surname_list, name) if 0 < i <= len(self.surname_list): if self.surname_list[i-1] != name: @@ -1340,7 +1339,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): If not then we need to remove the name from the list. The function must be overridden in the derived class. """ - name = str(person.get_primary_name().get_surname()) + name = str(find_surname_name(person.handle, + person.get_primary_name().serialize())) try: cursor = self.surnames.cursor(txn=self.txn) cursor.set(name) @@ -1400,7 +1400,10 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.genderStats.count_person(person) # Update surname list if necessary - if (old_person.primary_name.surname !=person.primary_name.surname): + if (find_surname_name(old_person.handle, + old_person.primary_name.serialize()) != + find_surname_name(person.handle, + person.primary_name.serialize())): self.remove_from_surname_list(old_person) self.add_to_surname_list(person, transaction.batch) else: diff --git a/src/plugins/import/ImportGrdb.py b/src/plugins/import/ImportGrdb.py index 5a8859820..f14c64553 100644 --- a/src/plugins/import/ImportGrdb.py +++ b/src/plugins/import/ImportGrdb.py @@ -77,8 +77,9 @@ def find_surname(key, data): """ Return the surname from the data stream. Used for building a secondary index. + This function is not needed, as we don't use the secondary index. """ - return str(data[3][5]) + return str("a") def find_idmap(key, data): """ @@ -138,6 +139,10 @@ class GrampsBSDDB(DbGrdb, UpdateCallback): This is replaced for internal use by gen/db/dbdir.py However, this class is still used for import of the 2.2.x GRDB format. In 3.0+ this format is no longer used. + + We only need to upgrade the old main tables. + That will be used to append data to the database this GrampsBSDDB is + imported to. """ def __init__(self, use_txn = True): @@ -1247,19 +1252,6 @@ class GrampsBSDDB(DbGrdb, UpdateCallback): self.surname_list = list(set(self.surnames.keys())) self.sort_surname_list() - def remove_from_surname_list(self, person): - """ - Check whether there are persons with the same surname left in - the database. If not then we need to remove the name from the list. - The function must be overridden in the derived class. - """ - name = str(person.get_primary_name().get_surname()) - try: - if self.surnames.keys().count(name) == 1: - self.surname_list.remove(unicode(name)) - except ValueError: - pass - def __get_obj_from_gramps_id(self, val, tbl, class_init, prim_tbl): if tbl.has_key(str(val)): #if str(val) in tbl: @@ -1563,6 +1555,8 @@ class GrampsBSDDB(DbGrdb, UpdateCallback): self.gramps_upgrade_13() if version < 14: self.gramps_upgrade_14() + if version < 15: + self.gramps_upgrade_15() LOG.debug("Upgrade time: %s %s", int(time.time()-t), "seconds") def gramps_upgrade_10(self): @@ -2589,6 +2583,108 @@ class GrampsBSDDB(DbGrdb, UpdateCallback): name_type, prefix, patronymic, group_as, sort_as, display_as, call) + def gramps_upgrade_15(self): + """Upgrade database from version 14 to 15. This upgrade adds: + * tagging + * surname list + """ + length = len(self.person_map)+10 + self.set_total(length) + + # --------------------------------- + # Modify Person + # --------------------------------- + for handle in self.person_map.keys(): + person = self.person_map[handle] + (junk_handle, # 0 + gramps_id, # 1 + gender, # 2 + primary_name, # 3 + alternate_names, # 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + urls, # 13 + ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + pprivate, # 19 + person_ref_list, # 20 + ) = person + + new_primary_name = self.convert_name_15(primary_name) + new_alternate_names = [self.convert_name_15(altname) for altname in + alternate_names] + new_person = (junk_handle, # 0 + gramps_id, # 1 + gender, # 2 + new_primary_name, # 3 + new_alternate_names,# 4 + death_ref_index, # 5 + birth_ref_index, # 6 + event_ref_list, # 7 + family_list, # 8 + parent_family_list, # 9 + media_list, # 10 + address_list, # 11 + attribute_list, # 12 + urls, # 13 + ord_list, # 14 + psource_list, # 15 + pnote_list, # 16 + change, # 17 + marker, # 18 + pprivate, # 19 + person_ref_list, # 20 + [] # 21, tags + ) + the_txn = self.env.txn_begin() + self.person_map.put(str(handle), new_person, txn=the_txn) + the_txn.commit() + self.update(length) + + #surname is now different, normally remove secondary index with names + #we skip this, as this database will not be used after the import + + # Bump up database version. Separate transaction to save metadata. + the_txn = self.env.txn_begin() + self.metadata.put('version', 15, txn=the_txn) + the_txn.commit() + + def convert_name_15(self, name): + (privacy, source_list, note_list, date, + first_name, surname, suffix, title, + name_type, prefix, patronymic, + group_as, sort_as, display_as, call) = name + + connector = u"" + origintype = (NameOriginType.NONE, u"") + patorigintype = (NameOriginType.PATRONYMIC, u"") + + if patronymic.strip() == u"": + #no patronymic, create a single surname + surname_list = [(surname, prefix, True, origintype, connector)] + else: + #a patronymic, if no surname or equal as patronymic, a single surname + if (surname.strip() == u"") or (surname == patronymic and prefix == u""): + surname_list = [(patronymic, prefix, True, patorigintype, connector)] + else: + #two surnames, first patronymic, then surname which is primary + surname_list = [(patronymic, u"", False, patorigintype, u""), + (surname, prefix, True, origintype, connector)] + + #return new value, add two empty strings for nick and family nick + return (privacy, source_list, note_list, date, + first_name, surname_list, suffix, title, name_type, + group_as, sort_as, display_as, call, u"", u"") + def set_auto_remove(self): """ BSDDB change log settings using new method with renamed attributes diff --git a/src/plugins/tool/Check.py b/src/plugins/tool/Check.py index fbba915f6..ba224c109 100644 --- a/src/plugins/tool/Check.py +++ b/src/plugins/tool/Check.py @@ -878,6 +878,7 @@ class CheckIntegrity(object): marker, # 18 pprivate, # 19 person_ref_list, # 20 + tags, # 21 ) = person # Take apart person reference list: new_person_ref_list = [] @@ -935,6 +936,7 @@ class CheckIntegrity(object): marker, # 18 pprivate, # 19 new_person_ref_list, # 20 + tags, # 21 ) p = gen.lib.Person(new_person) self.db.commit_person(p, self.trans) From 5013162ded62ab51b4021837a0ce37b7a5f01632 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sun, 3 Oct 2010 11:39:47 +0000 Subject: [PATCH 12/28] A new person editor svn: r15951 --- src/config.py | 2 - src/gen/db/base.py | 7 + src/gen/db/read.py | 8 + src/gen/db/write.py | 10 +- src/gen/proxy/proxybase.py | 5 + src/glade/editperson.glade | 1129 +++++++++++++++++++-------------- src/gui/editors/editperson.py | 99 +-- src/plugins/lib/libgrdb.py | 1 + 8 files changed, 754 insertions(+), 507 deletions(-) diff --git a/src/config.py b/src/config.py index 91a7ff85e..49eebec0e 100644 --- a/src/config.py +++ b/src/config.py @@ -211,7 +211,6 @@ register('interface.note-height', 500) register('interface.note-sel-height', 450) register('interface.note-sel-width', 600) register('interface.note-width', 700) -register('interface.patro-title', 0) register('interface.pedview-layout', 0) register('interface.pedview-show-images', True) register('interface.pedview-show-marriage', False) @@ -228,7 +227,6 @@ register('interface.place-height', 450) register('interface.place-sel-height', 450) register('interface.place-sel-width', 600) register('interface.place-width', 650) -register('interface.prefix-suffix', 0) register('interface.repo-height', 450) register('interface.repo-ref-height', 450) register('interface.repo-ref-width', 600) diff --git a/src/gen/db/base.py b/src/gen/db/base.py index 9e407f17b..014853cb1 100644 --- a/src/gen/db/base.py +++ b/src/gen/db/base.py @@ -361,6 +361,13 @@ class DbReadBase(object): """ raise NotImplementedError + def get_origin_types(self): + """ + Return a list of all custom origin types associated with Person/Surname + instances in the database. + """ + raise NotImplementedError + def get_note_bookmarks(self): """ Return the list of Note handles in the bookmarks. diff --git a/src/gen/db/read.py b/src/gen/db/read.py index 63db74f70..09158c587 100644 --- a/src/gen/db/read.py +++ b/src/gen/db/read.py @@ -297,6 +297,7 @@ class DbBsddbRead(DbReadBase, Callback): self.family_rel_types = set() self.event_role_names = set() self.name_types = set() + self.origin_types = set() self.repository_types = set() self.note_types = set() self.source_media_types = set() @@ -1267,6 +1268,13 @@ class DbBsddbRead(DbReadBase, Callback): """ return list(self.name_types) + def get_origin_types(self): + """ + Return a list of all custom origin types assocated with Person/Surname + instances in the database. + """ + return list(self.origin_types) + def get_repository_types(self): """ Return a list of all custom repository types assocated with Repository diff --git a/src/gen/db/write.py b/src/gen/db/write.py index 8966498fe..385475494 100644 --- a/src/gen/db/write.py +++ b/src/gen/db/write.py @@ -555,6 +555,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.family_rel_types = set(meta('family_rels')) self.event_role_names = set(meta('event_roles')) self.name_types = set(meta('name_types')) + self.origin_types = set(meta('origin_types')) self.repository_types = set(meta('repo_types')) self.note_types = set(meta('note_types')) self.source_media_types = set(meta('sm_types')) @@ -980,6 +981,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): txn.put('family_rels', list(self.family_rel_types)) txn.put('event_roles', list(self.event_role_names)) txn.put('name_types', list(self.name_types)) + txn.put('origin_types', list(self.origin_types)) txn.put('repo_types', list(self.repository_types)) txn.put('note_types', list(self.note_types)) txn.put('sm_types', list(self.source_media_types)) @@ -1425,7 +1427,13 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): for name in ([person.primary_name] + person.alternate_names) if name.type.is_custom()]) - + + all_surn = person.primary_name.get_surname_list() + for asurname in person.alternate_names: + all_surn += asurname.get_surname_list() + self.origin_types.update([str(surn.origintype) for surn in all_surn + if surn.origintype.is_custom()]) + self.url_types.update([str(url.type) for url in person.urls if url.type.is_custom()]) diff --git a/src/gen/proxy/proxybase.py b/src/gen/proxy/proxybase.py index be358e235..a56ac73d7 100644 --- a/src/gen/proxy/proxybase.py +++ b/src/gen/proxy/proxybase.py @@ -581,6 +581,11 @@ class ProxyDbBase(DbReadBase): instances in the database""" return self.db.get_name_types() + def get_origin_types(self): + """returns a list of all custom origin types associated with Person/Surname + instances in the database""" + return self.db.get_origin_types() + def get_repository_types(self): """returns a list of all custom repository types associated with Repository instances in the database""" diff --git a/src/glade/editperson.glade b/src/glade/editperson.glade index dab9f8f59..3550b211a 100644 --- a/src/glade/editperson.glade +++ b/src/glade/editperson.glade @@ -16,563 +16,762 @@ True - + True - 7 - 6 - 12 - 6 - - True - 0 - _Family: - True - center - surname - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - - True - 0 - Gi_ven: - True - center - given_name - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - 0 - _Gender: - True - gender - - - 1 - 2 - 5 - 6 - GTK_FILL - - - - - + True - + True - 0 - <b>Preferred name</b> - True + + + True + + + True + 0 + <b>Preferred name</b> + True + + + False + False + 0 + + + + + True + 0 + <b> - </b> + True + + + False + False + 1 + + + + + True + True + True + + + + True + gtk-edit + + + + + False + False + 2 + + + + + True + + + 3 + + + + + True + 1 + 4 + _Type: + True + center + ntype + + + False + 8 + 4 + + + + + True + + + 5 + + + + + + + + False + False + 3 + 0 + + + + + True + 0.029999999329447746 + + + True + 12 + + + True + 2 + 6 + 7 + 7 + + + True + 0 + Gi_ven: + True + center + given_name + + + GTK_FILL + + + + + + True + 0 + _Title: + True + title + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Suffi_x: + True + suffix + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + Call _Name: + True + call + + + 4 + 5 + GTK_FILL + + + + + + True + True + The person's given names + + + + + 1 + 4 + + + + + + True + 0 + N_ick Name: + True + nickname + + + 4 + 5 + 1 + 2 + GTK_FILL + + + + + + True + True + Part of the Given name that is the normally used name. + + + + 5 + 6 + + + + + True + True + A title used to refer to the person, such as 'Dr.' or 'Rev.' + + + + 1 + 2 + 1 + 2 + + + + + True + True + An optional suffix to the name, such as "Jr." or "III" + + + + 3 + 4 + 1 + 2 + + + + + True + True + A descriptive name given in place of or in addition to the official given name. + + + + 5 + 6 + 1 + 2 + + + + + + + + + True + <i>Given Name(s) </i> + True + + + + + False + False + 3 + 1 + + - False - False 0 - + + 124 True - 0 - <b> - </b> - True - - - False - False - 1 - - - - - True - True - False - Edit the preferred name - + 2.2351741291171123e-10 - + True - gtk-edit + + + 120 + 100 + True + + + + + + + True + <b>Image</b> + True - False - False - 2 - - - - - 4 - GTK_FILL - GTK_FILL - - - - - True - 0 - _Type: - True - center - ntype - - - 1 - 2 - 3 - 4 - GTK_FILL - - - - - - True - True - part of a person's name indicating the family to which the person belongs - - - 2 - 3 - 1 - 2 - - - - - - True - True - True - The person's given name - - - - 2 - 3 - 2 - 3 - - - - - - True - - - 2 - 3 - 3 - 4 - GTK_FILL - GTK_FILL - - - - - True - liststore2 - - - - 0 - - - - - 2 - 3 - 5 - 6 - GTK_FILL - GTK_FILL - - - - - True - - - True - - - 0 - - - - - True - : - prefixentry - - - False - False + 5 1 - 3 - 4 - 1 - 2 - GTK_FILL - GTK_FILL + False + 0 - + True - 0 - Call _Name: - True - call + 0.029999999329447746 + none + + + True + 12 + + + True + 5 + + + True + 2 + 4 + 7 + 4 + + + True + _Prefix + True + prefix + + + GTK_FILL + + + + + True + _Surname + True + surname + + + 1 + 2 + GTK_FILL + + + + + True + _Origin + True + cmborigin + + + 2 + 3 + GTK_FILL + + + + + True + True + part of a person's name indicating the family to which the person belongs + + + + 1 + 2 + 1 + 2 + + + + + + True + The origin of the family name of this family, eg 'Inherited' or 'Patronymic'. + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + Use _Multiple Surnames + True + True + True + True + + + 3 + 4 + 1 + 2 + + + + + + + True + True + An optional prefix for the family that is not used in sorting, such as "de" or "van". + + + + 1 + 2 + GTK_FILL + + + + + True + + + 3 + 4 + + + + + + + False + False + 0 + + + + + + + + + True + <i>Family Name </i> + True + + - 3 - 4 - 2 - 3 - GTK_FILL - + False + False + 2 + 1 - + True + 0.029999999329447746 + none - + True - liststore1 + 12 - + + True + + + + + + + + + + + + + True + <i>Family Names </i> + True + + + + + False + False + 2 + 2 + + + + + True + 3 + 6 + 9 + 4 + + + True + 12 + + + True + 0 + <b>General</b> + True + + + False + False + 0 + + + + + True + True + True + none + + + True + gramps-unlock + + + + + False + False + 1 + + + + + 4 + GTK_FILL + + + + + + True + liststore2 + + 0 - 0 + 2 + 3 + 1 + 2 + GTK_FILL + GTK_FILL - + True - : - patroentry - - - False - False - 1 - - - - - 3 - 4 - 3 - 4 - GTK_FILL - GTK_FILL - - - - - True - 12 - - - True - 0 - <b>General</b> - True - - - False - False - 0 - - - - - True - True - False - Indicates if the record is private - none + 5 - + True - gramps-unlock + 0 + _ID: + True + right + gid + + False + False + 0 + + + + + True + True + A unique ID of the person. + + 6 + + + 1 + + + + + True + 0 + _Marker: + True + marker + + + False + False + 2 + + + + + True + + + 3 + - False - False - 1 + 3 + 6 + 1 + 2 + GTK_FILL + - - - 4 - 4 - 5 - GTK_FILL - GTK_FILL - - - - - True - True - Prefix: An optional prefix for the family name that is not used in sorting, such as "de" or "van" -Suffix: An optional suffix to the name, such as "Jr." or "III" - 6 - - - 4 - 5 - 1 - 2 - - - - - - True - True - Part of the Given name that is the normally used name. - - 8 - - - 4 - 5 - 2 - 3 - - - - - - True - True - Patronimic: component of a personal name based on the name of one's father, grandfather, .... -Title: A title used to refer to the person, such as 'Dr.' or 'Rev.' - 8 - - - 4 - 5 - 3 - 4 - - - - - - True - 5 - + True 0 - _ID: + _Tags: True - right - gid - False - False - 0 + 1 + 2 + 2 + 3 + GTK_FILL + - + True - True - - 6 + + + True + + + 0 + + + + + True + True + True + + + True + + + + + False + False + 1 + + - 1 + 2 + 6 + 2 + 3 + - + True 0 - _Marker: + _Gender: True - marker + gender - False - False - 2 + 1 + 2 + 1 + 2 + GTK_FILL + - + + + + + + + True + 10 - 3 + 1 + 2 + + + + + + + True + 10 + + + 2 + 3 + + - 3 - 6 - 5 - 6 - GTK_FILL - GTK_FILL + False + False + 3 - - - 124 - True - 0 - - - True - - - 120 - 100 - True - - - - - - - True - <b>Image</b> - True - - - - - 5 - 6 - 4 - GTK_FILL - GTK_FILL - - - - - True - 0 - _Tags: - True - - - 1 - 2 - 6 - 7 - - - - - True - - - True - - - 0 - - - - - True - True - True - - - True - gramps-tag - - - - - False - False - 1 - - - - - 2 - 6 - 6 - 7 - - - - - - - - - - - - - - - - - - - - - - - - - - False False - 10 1 diff --git a/src/gui/editors/editperson.py b/src/gui/editors/editperson.py index de08b8da1..55ace943e 100644 --- a/src/gui/editors/editperson.py +++ b/src/gui/editors/editperson.py @@ -142,9 +142,11 @@ class EditPerson(EditPrimary): self.obj_photo = self.top.get_object("personPix") self.eventbox = self.top.get_object("eventbox1") + self.singsurnfr = self.top.get_object("surnamefr") + self.multsurnfr = self.top.get_object("multsurnamefr") + self.singlesurn_active = True self.set_contexteventbox(self.top.get_object("eventboxtop")) - def _post_init(self): """ @@ -157,10 +159,13 @@ class EditPerson(EditPrimary): """ self.load_person_image() - if self.pname.get_surname() and not self.pname.get_first_name(): - self.given.grab_focus() - else: - self.surname_field.grab_focus() + self.given.grab_focus() + + self.multsurnfr.hide_all() + #if self.pname.get_surname() and not self.pname.get_first_name(): + # self.given.grab_focus() + #else: + # self.surname_field.grab_focus() def _connect_signals(self): """ @@ -172,7 +177,7 @@ class EditPerson(EditPrimary): self.define_help_button(self.top.get_object("button134")) self.given.connect("focus_out_event", self._given_focus_out_event) - self.top.get_object("button177").connect("clicked", + self.top.get_object("editnamebtn").connect("clicked", self._edit_name_clicked) self.eventbox.connect('button-press-event', @@ -228,6 +233,11 @@ class EditPerson(EditPrimary): # we just rebuild the view always self.event_list.rebuild_callback() + def _validate_call(self, widget, text): + """ a callname must be a part of the given name, see if this is the + case """ + return text in self.given.obj.get_text().split() + def _setup_fields(self): """ Connect the GrampsWidget objects to field in the interface. @@ -267,44 +277,56 @@ class EditPerson(EditPrimary): self.pname.get_type, self.db.readonly, self.db.get_name_types()) - - self.prefix_suffix = widgets.MonitoredComboSelectedEntry( - self.top.get_object("prefixcmb"), - self.top.get_object("prefixentry"), - [_('Prefix'), _('Suffix')], - [self.pname.set_surname_prefix, self.pname.set_suffix], - [self.pname.get_surname_prefix, self.pname.get_suffix], - default = config.get('interface.prefix-suffix'), - read_only = self.db.readonly) - self.patro_title = widgets.MonitoredComboSelectedEntry( - self.top.get_object("patrocmb"), - self.top.get_object("patroentry"), - [_('Patronymic'), _('Person|Title')], - [self.pname.set_patronymic, self.pname.set_title], - [self.pname.get_patronymic, self.pname.get_title], - default = config.get('interface.patro-title'), - read_only = self.db.readonly) - - self.call = widgets.MonitoredEntry( - self.top.get_object("call"), - self.pname.set_call_name, - self.pname.get_call_name, - self.db.readonly) - + #part of Given Name section self.given = widgets.MonitoredEntry( self.top.get_object("given_name"), self.pname.set_first_name, self.pname.get_first_name, self.db.readonly) + self.call = widgets.MonitoredEntry( + self.top.get_object("call"), + self.pname.set_call_name, + self.pname.get_call_name, + self.db.readonly) + self.call.connect("validate", self._validate_call) + + self.title = widgets.MonitoredEntry( + self.top.get_object("title"), + self.pname.set_title, + self.pname.get_title, + self.db.readonly) + + self.suffix = widgets.MonitoredEntry( + self.top.get_object("suffix"), + self.pname.set_suffix, + self.pname.get_suffix, + self.db.readonly) + + #part of Single Surname section self.surname_field = widgets.MonitoredEntry( self.top.get_object("surname"), - self.pname.set_surname, - self.pname.get_surname, + self.pname.get_primary_surname().set_surname, + self.pname.get_primary_surname().get_surname, self.db.readonly, autolist=self.db.get_surname_list() if not self.db.readonly else []) + self.prefix = widgets.MonitoredEntry( + self.top.get_object("prefix"), + self.pname.get_primary_surname().set_prefix, + self.pname.get_primary_surname().get_prefix, + self.db.readonly) + + self.ortype_field = widgets.MonitoredDataType( + self.top.get_object("cmborigin"), + self.pname.get_primary_surname().set_origintype, + self.pname.get_primary_surname().get_origintype, + self.db.readonly, + self.db.get_origin_types()) + + #other fields + self.tags = widgets.MonitoredTagList( self.top.get_object("tag_label"), self.top.get_object("tag_button"), @@ -321,11 +343,11 @@ class EditPerson(EditPrimary): self.db.readonly) #make sure title updates automatically - for obj in [self.top.get_object("surname"), - self.top.get_object("given_name"), - self.top.get_object("patroentry"), + for obj in [self.top.get_object("given_name"), self.top.get_object("call"), - self.top.get_object("prefixentry"), + self.top.get_object("suffix"), + self.top.get_object("prefix"), + self.top.get_object("surname"), ]: obj.connect('changed', self._changed_name) @@ -894,9 +916,8 @@ class EditPerson(EditPrimary): return child_ref_list def _cleanup_on_exit(self): - config.set('interface.prefix-suffix', self.prefix_suffix.active_key) - config.set('interface.patro-title', self.patro_title.active_key) - config.save() + pass + #config.save() class GenderDialog(gtk.MessageDialog): diff --git a/src/plugins/lib/libgrdb.py b/src/plugins/lib/libgrdb.py index c7f70ee74..e165380de 100644 --- a/src/plugins/lib/libgrdb.py +++ b/src/plugins/lib/libgrdb.py @@ -211,6 +211,7 @@ class DbGrdb(Callback): self.family_rel_types = set() self.event_role_names = set() self.name_types = set() + self.origin_types = set() self.repository_types = set() self.note_types = set() self.source_media_types = set() From b3d8e3c857b20fa35747b29144f88c405722973e Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 4 Oct 2010 16:13:38 +0000 Subject: [PATCH 13/28] Working Edit Person dialog, no multiple surname yet. svn: r15952 --- src/gui/editors/editperson.py | 19 +++++++++++++++++-- src/plugins/lib/libpersonview.py | 5 ++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/gui/editors/editperson.py b/src/gui/editors/editperson.py index 55ace943e..7d7d218ab 100644 --- a/src/gui/editors/editperson.py +++ b/src/gui/editors/editperson.py @@ -58,6 +58,7 @@ from editmediaref import EditMediaRef from editname import EditName import config from QuestionDialog import ErrorDialog, ICON +from Errors import ValidationError from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, @@ -103,7 +104,11 @@ class EditPerson(EditPrimary): This is used by the base class (EditPrimary). """ - return gen.lib.Person() + person = gen.lib.Person() + #the editor requires a surname + person.primary_name.add_surname(gen.lib.Surname()) + person.primary_name.set_primary_surname(0) + return person def get_menu_title(self): if self.obj and self.obj.get_handle(): @@ -236,7 +241,9 @@ class EditPerson(EditPrimary): def _validate_call(self, widget, text): """ a callname must be a part of the given name, see if this is the case """ - return text in self.given.obj.get_text().split() + if not text in self.given.obj.get_text().split(): + return ValidationError(_("Call name must be the given name that " + "is normally used.")) def _setup_fields(self): """ @@ -291,6 +298,8 @@ class EditPerson(EditPrimary): self.pname.get_call_name, self.db.readonly) self.call.connect("validate", self._validate_call) + #force validation now with initial entry + self.call.obj.validate(force=True) self.title = widgets.MonitoredEntry( self.top.get_object("title"), @@ -304,6 +313,12 @@ class EditPerson(EditPrimary): self.pname.get_suffix, self.db.readonly) + self.nick = widgets.MonitoredEntry( + self.top.get_object("nickname"), + self.pname.set_nick_name, + self.pname.get_nick_name, + self.db.readonly) + #part of Single Surname section self.surname_field = widgets.MonitoredEntry( self.top.get_object("surname"), diff --git a/src/plugins/lib/libpersonview.py b/src/plugins/lib/libpersonview.py index 189f117ab..2f8a007b3 100644 --- a/src/plugins/lib/libpersonview.py +++ b/src/plugins/lib/libpersonview.py @@ -250,9 +250,12 @@ class BasePersonView(ListView): def add(self, obj): person = gen.lib.Person() + #the editor requires a surname + person.primary_name.add_surname(gen.lib.Surname()) + person.primary_name.set_primary_surname(0) try: - EditPerson(self.dbstate, self.uistate, [], gen.lib.Person()) + EditPerson(self.dbstate, self.uistate, [], person) except Errors.WindowActiveError: pass From 90379b7b42eef580d526456450d51465eb143c24 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 15 Oct 2010 21:42:56 +0000 Subject: [PATCH 14/28] continue adding to the editname GUI svn: r15999 --- po/POTFILES.in | 2 + src/DdTargets.py | 2 + src/ScratchPad.py | 13 + src/glade/editname.glade | 585 +++++++++++--------- src/glade/editperson.glade | 22 +- src/gui/editors/displaytabs/Makefile.am | 2 + src/gui/editors/displaytabs/__init__.py | 1 + src/gui/editors/displaytabs/surnamemodel.py | 55 ++ src/gui/editors/displaytabs/surnametab.py | 214 +++++++ src/gui/editors/editname.py | 70 ++- src/gui/editors/editperson.py | 42 +- src/gui/editors/editsecondary.py | 12 +- 12 files changed, 706 insertions(+), 314 deletions(-) create mode 100644 src/gui/editors/displaytabs/surnamemodel.py create mode 100644 src/gui/editors/displaytabs/surnametab.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 407c02ffd..44acc682c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -273,6 +273,8 @@ src/gui/editors/displaytabs/reporefmodel.py src/gui/editors/displaytabs/sourcebackreflist.py src/gui/editors/displaytabs/sourceembedlist.py src/gui/editors/displaytabs/sourcerefmodel.py +src/gui/editors/displaytabs/surnametab.py +src/gui/editors/displaytabs/surnamemodel.py src/gui/editors/displaytabs/webembedlist.py src/gui/editors/displaytabs/webmodel.py src/gui/editors/displaytabs/__init__.py diff --git a/src/DdTargets.py b/src/DdTargets.py index fd759ff38..3f588be27 100644 --- a/src/DdTargets.py +++ b/src/DdTargets.py @@ -133,6 +133,7 @@ class _DdTargets(object): self.SOURCEREF = _DdType(self, 'srcref') self.SOURCE_LINK = _DdType(self, 'source-link') self.URL = _DdType(self, 'url') + self.SURNAME = _DdType(self, 'surname') # List of all types that are used between # gramps widgets but should not be exported @@ -159,6 +160,7 @@ class _DdTargets(object): self.SOURCEREF, self.SOURCE_LINK, self.URL, + self.SURNAME ] self.CHILD = _DdType(self, 'child') diff --git a/src/ScratchPad.py b/src/ScratchPad.py index 181a6734d..48ec0eb63 100644 --- a/src/ScratchPad.py +++ b/src/ScratchPad.py @@ -507,6 +507,19 @@ class ScratchName(ScratchObjWrapper): self._title = str(self._obj.get_type()) self._value = self._obj.get_name() +class ScratchSurname(ScratchObjWrapper): + + DROP_TARGETS = [DdTargets.SURNAME] + DRAG_TARGET = DdTargets.SURNAME + ICON = ICONS['name'] + + def __init__(self, dbstate, obj): + super(ScratchSurname, self).__init__(dbstate, obj) + self._type = _("Surname") + if self._obj: + self._title = self._obj.get_surname() + self._value = self._obj.get_surname() + class ScratchText(ScratchWrapper): DROP_TARGETS = DdTargets.all_text() diff --git a/src/glade/editname.glade b/src/glade/editname.glade index aefc9a1c3..24c8ed810 100644 --- a/src/glade/editname.glade +++ b/src/glade/editname.glade @@ -12,275 +12,331 @@ True - vertical - + True - 12 - 4 - 5 - 12 - 6 - + True - 0 - _Given: - True - center - alt_given - - - 1 - 2 - GTK_FILL - - - - - - True - 0 - P_atronymic: - True - patronymic - - - 3 - 4 - GTK_FILL - - - - - - True - True - - - - 1 - 2 - 3 - 4 - - - - - - True - 0 - _Family: - True - center - alt_surname - - - GTK_FILL - - - - - - True - True - - - - 1 - 2 - - - - - - True - 0 - _Prefix: - True - center - alt_prefix - - - 2 - 3 - GTK_FILL - - - - - - True - 0 - Tit_le: - True - center - alt_title - - - 2 - 3 - GTK_FILL - - - - - - True - True - - - - 1 - 2 - 2 - 3 - - - - - - True - 0 - Suffi_x: - True - center - alt_suffix - - - 2 - 3 - 2 - 3 - GTK_FILL - - - - - - True - 0 - 0 - 1 - _Type: - True - center - name_type - - - 2 - 3 - 3 - 4 - GTK_FILL - - - - - - True - True - - - - 3 - 5 - - - - - - True - - - 3 - 5 - 3 - 4 - - - - - - True - True - - - - 3 - 4 - 2 - 3 - GTK_FILL - - - - - - True - True - True - none - + True - gtk-dialog-authentication - 1 + 0 + 4 + _Type: + True + center + ntype + + + False + 8 + 0 + + + + + True + + + 1 + + + + + + + + + + + + + + + + + True + True + True + none + + + True + gtk-dialog-authentication + 1 + + + + + end + 6 + + + + + False + False + 3 + 0 + + + + + + + + True + 0.029999999329447746 + + + True + 12 + + + True + 2 + 6 + 7 + 7 + + + True + 0 + _Given: + True + center + given_name + + + GTK_FILL + + + + + + True + 0 + T_itle: + True + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + Suffi_x: + True + suffix + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + 0 + C_all Name: + True + call + + + 4 + 5 + GTK_FILL + + + + + + True + True + True + True + + + + 1 + 4 + + + + + + True + 0 + _Nick Name: + True + nickname + + + 4 + 5 + 1 + 2 + GTK_FILL + + + + + + True + True + + + + 5 + 6 + + + + + True + True + + + + 1 + 2 + 1 + 2 + + + + + True + True + + + + 3 + 4 + 1 + 2 + + + + + True + True + + + + 5 + 6 + 1 + 2 + + + + + + + + + True + <i>Given Name(s) </i> + True - 4 - 5 - 2 - 3 - GTK_FILL - + False + False + 3 + 2 - + True - True - + 0.029999999329447746 + none + + + True + 12 + + + True + + + True + + + + + + + + + 0 + + + + + True + + + True + 0 + _Family Nick Name: + True + familynickname + + + False + 0 + + + + + True + True + + 25 + + + False + 3 + 1 + + + + + 3 + 1 + + + + + + + + + True + <i>Family Names </i> + True + + - 1 - 2 - 1 - 2 - - - - - - True - 0 - Call _Name: - True - call - - - 2 - 3 - 1 - 2 - GTK_FILL - - - - - - True - True - - - - 3 - 5 - 1 - 2 - + False + False + 2 + 3 @@ -288,6 +344,12 @@ 0 + + + + + + True @@ -367,7 +429,6 @@ True - liststore2 @@ -405,7 +466,6 @@ True - liststore1 @@ -555,7 +615,7 @@ - 1 + 3 @@ -591,7 +651,6 @@ True True True - True True True Accept changes and close window @@ -634,16 +693,4 @@ button131 - - - - - - - - - - - - diff --git a/src/glade/editperson.glade b/src/glade/editperson.glade index 3550b211a..73d660b24 100644 --- a/src/glade/editperson.glade +++ b/src/glade/editperson.glade @@ -134,7 +134,7 @@ True 0 - Gi_ven: + _Given: True center given_name @@ -148,7 +148,7 @@ True 0 - _Title: + T_itle: True title @@ -180,7 +180,7 @@ True 0 - Call _Name: + C_all Name: True call @@ -209,7 +209,7 @@ True 0 - N_ick Name: + _Nick Name: True nickname @@ -716,7 +716,7 @@ True 0 - _Gender: + G_ender: True gender @@ -729,12 +729,6 @@ - - - - - - True @@ -759,6 +753,12 @@ + + + + + + False diff --git a/src/gui/editors/displaytabs/Makefile.am b/src/gui/editors/displaytabs/Makefile.am index eaa2438f7..73a8619f2 100644 --- a/src/gui/editors/displaytabs/Makefile.am +++ b/src/gui/editors/displaytabs/Makefile.am @@ -42,6 +42,8 @@ pkgdata_PYTHON = \ sourcebackreflist.py \ sourceembedlist.py \ sourcerefmodel.py \ + surnamemodel.py \ + surnametab.py \ webembedlist.py \ webmodel.py \ __init__.py diff --git a/src/gui/editors/displaytabs/__init__.py b/src/gui/editors/displaytabs/__init__.py index b04413e1d..119114911 100644 --- a/src/gui/editors/displaytabs/__init__.py +++ b/src/gui/editors/displaytabs/__init__.py @@ -55,6 +55,7 @@ from personrefembedlist import PersonRefEmbedList from personbackreflist import PersonBackRefList from placebackreflist import PlaceBackRefList from repoembedlist import RepoEmbedList +from surnametab import SurnameTab from sourcebackreflist import SourceBackRefList from sourceembedlist import SourceEmbedList from webembedlist import WebEmbedList diff --git a/src/gui/editors/displaytabs/surnamemodel.py b/src/gui/editors/displaytabs/surnamemodel.py new file mode 100644 index 000000000..2c2ff64fc --- /dev/null +++ b/src/gui/editors/displaytabs/surnamemodel.py @@ -0,0 +1,55 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2010 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# GTK libraries +# +#------------------------------------------------------------------------- +import gtk +import gobject + +#------------------------------------------------------------------------- +# +# GRAMPS classes +# +#------------------------------------------------------------------------- + + +#------------------------------------------------------------------------- +# +# SurnamModel +# +#------------------------------------------------------------------------- +class SurnameModel(gtk.ListStore): + + def __init__(self, surn_list, db): + #setup model for the treeview + gtk.ListStore.__init__(self, str, str, str, gobject.TYPE_PYOBJECT, + bool, object) + for surn in surn_list: + # fill the liststore + self.append(row=[surn.get_prefix(), surn.get_surname(), + surn.get_connector(), surn.get_origintype(), + surn.get_primary(), surn]) + self.db = db diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py new file mode 100644 index 000000000..e2510487f --- /dev/null +++ b/src/gui/editors/displaytabs/surnametab.py @@ -0,0 +1,214 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2010 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# Python classes +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GTK classes +# +#------------------------------------------------------------------------- +import gtk +_TAB = gtk.gdk.keyval_from_name("Tab") +_ENTER = gtk.gdk.keyval_from_name("Enter") + +#------------------------------------------------------------------------- +# +# GRAMPS classes +# +#------------------------------------------------------------------------- +from surnamemodel import SurnameModel +from embeddedlist import EmbeddedList +from DdTargets import DdTargets +from gen.lib import Surname, NameOriginType + +#------------------------------------------------------------------------- +# +# SurnameTab +# +#------------------------------------------------------------------------- +class SurnameTab(EmbeddedList): + + _HANDLE_COL = 5 + _DND_TYPE = DdTargets.SURNAME + + _MSG = { + 'add' : _('Create and add a new surname'), + 'del' : _('Remove the selected surname'), + 'edit' : _('Edit the selected surname'), + 'up' : _('Move the selected surname upwards'), + 'down' : _('Move the selected surname downwards'), + } + + #index = column in model. Value = + # (name, sortcol in model, width, markup/text + _column_names = [ + (_('Prefix'), 0, 150, 0, -1), + (_('Surname'), 1, 250, 0, -1), + (_('Connector'), 2, 100, 0, -1), + ] + + def __init__(self, dbstate, uistate, track, name): + self.obj = name + self.curr_col = -1 + self.curr_cellr = None + + EmbeddedList.__init__(self, dbstate, uistate, track, _('Family Surnames'), + SurnameModel, move_buttons=True) + + def build_columns(self): + #first the standard text columns with normal method + EmbeddedList.build_columns(self) + + # Need to add attributes to renderers + # and connect renderers to the 'edited' signal + for colno in range(len(self.columns)): + for renderer in self.columns[colno].get_cell_renderers(): + renderer.set_property('editable', not self.dbstate.db.readonly) + renderer.connect('editing_started', self.edit_start, colno) + renderer.connect('edited', self.edit_inline, colno) + + # now we add the two special columns + # TODO + + def get_data(self): + return self.obj.get_surname_list() + + def is_empty(self): + return len(self.model)==0 + + def _get_surn_from_model(self): + """ + Return new surname_list for storing in the name based on content of + the model + """ + new_list = [] + for idx in range(len(self.model)): + node = self.model.get_iter(idx) + surn = self.model.get_value(node, 5) + surn.set_prefix(unicode(self.model.get_value(node, 0))) + surn.set_surname(unicode(self.model.get_value(node, 1))) + surn.set_connector(unicode(self.model.get_value(node, 2))) + surn.set_primary(self.model.get_value(node, 4)) + new_list += [surn] + return new_list + + def update(self): + new_map = self._get_surn_from_model() + self.obj.set_surname_list(new_map) + # update name in title name editor + # TODO + + def column_order(self): + # order of columns for EmbeddedList. Only the text columns here + return ((1, 0), (1, 1), (1, 2)) + + def add_button_clicked(self, obj): + prim = False + if len(self.obj.get_surname_list()) == 0: + prim = true + node = self.model.append(row=['', '', '', NameOriginType(), prim, + Surname()]) + self.selection.select_iter(node) + path = self.model.get_path(node) + self.tree.set_cursor_on_cell(path, + focus_column=self.columns[0], + focus_cell=None, + start_editing=True) + + def del_button_clicked(self, obj): + (model, node) = self.selection.get_selected() + if node: + self.model.remove(node) + self.update() + + def edit_start(self, cellr, obj, path, colnr): + self.curr_col = colnr + self.curr_cellr = cellr + + def edit_inline(self, cell, path, new_text, colnr): + node = self.model.get_iter(path) + self.model.set_value(node, colnr, new_text) + self.update() + + def edit_button_clicked(self, obj): + (model, node) = self.selection.get_selected() + if node: + path = self.model.get_path(node) + self.tree.set_cursor_on_cell(path, + focus_column=self.columns[0], + focus_cell=None, + start_editing=True) + + def key_pressed(self, obj, event): + """ + Handles the key being pressed. + Here we make sure tab moves to next value in row + """ + if not EmbeddedList.key_pressed(self, obj, event): + if event.type == gtk.gdk.KEY_PRESS and event.keyval in (_TAB,): + if event.state not in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK): + self.next_cell() + elif event.state in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK): + self.prev_cell() + else: + return + elif event.type == gtk.gdk.KEY_PRESS and event.keyval in (_ENTER,): + self.next_cell() + else: + return + return True + + def next_cell(self): + """ + Move to the next cell to edit it + """ + print 'captured tab' + (model, node) = self.selection.get_selected() + if node: + path = self.model.get_path(node) + if self.curr_col+1 < len(self.columns): + self.tree.set_cursor_on_cell(path, + focus_column=self.columns[self.curr_col+1], + focus_cell=None, + start_editing=True) + elif self.curr_col+1 == len(self.columns): + #go to next line if there is one + if path[0]+1 < len(self.obj.get_surname_list()): + newpath = (path[0]+1,) + self.selection.select_path(newpath) + self.tree.set_cursor_on_cell(newpath, + focus_column=self.columns[0], + focus_cell=None, + start_editing=True) + else: + #stop editing + self.curr_cellr.stop_editing(True) + + def prev_cell(self): + print 'captured tab prev' diff --git a/src/gui/editors/editname.py b/src/gui/editors/editname.py index a486d6d98..5ca54378d 100644 --- a/src/gui/editors/editname.py +++ b/src/gui/editors/editname.py @@ -28,7 +28,9 @@ # Standard python modules # #------------------------------------------------------------------------- +import gobject from gen.ggettext import gettext as _ +from copy import copy #------------------------------------------------------------------------- # @@ -45,7 +47,7 @@ import gtk from gen.display.name import displayer as name_displayer from editsecondary import EditSecondary from gen.lib import NoteType -from displaytabs import GrampsTab,SourceEmbedList,NoteTab +from displaytabs import GrampsTab, SourceEmbedList, NoteTab, SurnameTab from gui.widgets import (MonitoredEntry, MonitoredMenu, MonitoredDate, MonitoredDataType, PrivacyButton) from glade import Glade @@ -111,12 +113,13 @@ class EditName(EditSecondary): self.top = Glade() - self.set_window(self.top.toplevel, - self.top.get_object("title"), - _("Name Editor")) + self.set_window(self.top.toplevel, None, _("Name Editor")) tblgnam = self.top.get_object('table23') notebook = self.top.get_object('notebook') + hbox_surn = self.top.get_object('hboxmultsurnames') + hbox_surn.pack_start(SurnameTab(self.dbstate, self.uistate, self.track, + self.obj)) #recreate start page as GrampsTab notebook.remove_page(0) self.gennam = GeneralNameTab(self.dbstate, self.uistate, self.track, @@ -147,7 +150,19 @@ class EditName(EditSecondary): def _connect_signals(self): self.define_cancel_button(self.top.get_object('button119')) self.define_help_button(self.top.get_object('button131')) - self.define_ok_button(self.top.get_object('button118'),self.save) + self.define_ok_button(self.top.get_object('button118'), self.save) + + def _validate_call(self, widget, text): + """ a callname must be a part of the given name, see if this is the + case """ + validcall = self.given_field.obj.get_text().split() + dummy = copy(validcall) + for item in dummy: + validcall += item.split('-') + if text in validcall: + return + return ValidationError(_("Call name must be the given name that " + "is normally used.")) def _setup_fields(self): self.group_as = MonitoredEntry( @@ -180,7 +195,7 @@ class EditName(EditSecondary): self.db.readonly) self.given_field = MonitoredEntry( - self.top.get_object("alt_given"), + self.top.get_object("given_name"), self.obj.set_first_name, self.obj.get_first_name, self.db.readonly) @@ -190,39 +205,42 @@ class EditName(EditSecondary): self.obj.set_call_name, self.obj.get_call_name, self.db.readonly) + self.call_field.connect("validate", self._validate_call) + #force validation now with initial entry + self.call_field.obj.validate(force=True) self.title_field = MonitoredEntry( - self.top.get_object("alt_title"), + self.top.get_object("title_field"), self.obj.set_title, self.obj.get_title, self.db.readonly) self.suffix_field = MonitoredEntry( - self.top.get_object("alt_suffix"), + self.top.get_object("suffix"), self.obj.set_suffix, self.obj.get_suffix, self.db.readonly) - self.patronymic_field = MonitoredEntry( - self.top.get_object("patronymic"), - self.obj.set_patronymic, - self.obj.get_patronymic, + self.nick = MonitoredEntry( + self.top.get_object("nickname"), + self.obj.set_nick_name, + self.obj.get_nick_name, self.db.readonly) - self.surname_field = MonitoredEntry( - self.top.get_object("alt_surname"), - self.obj.set_surname, - self.obj.get_surname, - self.db.readonly, - autolist=self.db.get_surname_list() if not self.db.readonly else [], - changed=self.update_group_as) - - self.prefix_field = MonitoredEntry( - self.top.get_object("alt_prefix"), - self.obj.set_surname_prefix, - self.obj.get_surname_prefix, + self.famnick = MonitoredEntry( + self.top.get_object("familynickname"), + self.obj.set_family_nick_name, + self.obj.get_family_nick_name, self.db.readonly) + #self.surname_field = MonitoredEntry( + # self.top.get_object("alt_surname"), + # self.obj.set_surname, + # self.obj.get_surname, + # self.db.readonly, + # autolist=self.db.get_surname_list() if not self.db.readonly else [], + # changed=self.update_group_as) + self.date = MonitoredDate( self.top.get_object("date_entry"), self.top.get_object("date_stat"), @@ -232,7 +250,7 @@ class EditName(EditSecondary): self.db.readonly) self.obj_combo = MonitoredDataType( - self.top.get_object("name_type"), + self.top.get_object("ntype"), self.obj.set_type, self.obj.get_type, self.db.readonly, @@ -322,7 +340,7 @@ class EditName(EditSecondary): surname = self.obj.get_surname() self.group_as.set_text(surname) - def save(self,*obj): + def save(self, *obj): """Save the name setting. All is ok, except grouping. We need to consider: 1/ global set, not local set --> unset (ask if global unset) diff --git a/src/gui/editors/editperson.py b/src/gui/editors/editperson.py index 7d7d218ab..ffb89487f 100644 --- a/src/gui/editors/editperson.py +++ b/src/gui/editors/editperson.py @@ -31,6 +31,7 @@ to edit information about a particular Person. # Standard python modules # #------------------------------------------------------------------------- +from copy import copy from gen.ggettext import sgettext as _ #------------------------------------------------------------------------- @@ -63,7 +64,7 @@ from Errors import ValidationError from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, WebEmbedList, PersonRefEmbedList, LdsEmbedList, - PersonBackRefList) + PersonBackRefList, SurnameTab) from gen.plug import CATEGORY_QR_PERSON #------------------------------------------------------------------------- @@ -150,6 +151,9 @@ class EditPerson(EditPrimary): self.singsurnfr = self.top.get_object("surnamefr") self.multsurnfr = self.top.get_object("multsurnamefr") self.singlesurn_active = True + self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, + self.obj.get_primary_name()) + self.top.get_object("hboxmultsurnames").pack_start(self.surntab) self.set_contexteventbox(self.top.get_object("eventboxtop")) @@ -166,7 +170,12 @@ class EditPerson(EditPrimary): self.load_person_image() self.given.grab_focus() - self.multsurnfr.hide_all() + if len(self.obj.get_primary_name().get_surname_list()) > 1: + self.singsurnfr.hide_all() + self.singlesurn_active = False + else: + self.multsurnfr.hide_all() + self.singlesurn_active = True #if self.pname.get_surname() and not self.pname.get_first_name(): # self.given.grab_focus() #else: @@ -184,6 +193,8 @@ class EditPerson(EditPrimary): self.given.connect("focus_out_event", self._given_focus_out_event) self.top.get_object("editnamebtn").connect("clicked", self._edit_name_clicked) + self.top.get_object("multsurnamebtn").connect("clicked", + self._mult_surn_clicked) self.eventbox.connect('button-press-event', self._image_button_press) @@ -241,8 +252,13 @@ class EditPerson(EditPrimary): def _validate_call(self, widget, text): """ a callname must be a part of the given name, see if this is the case """ - if not text in self.given.obj.get_text().split(): - return ValidationError(_("Call name must be the given name that " + validcall = self.given.obj.get_text().split() + dummy = copy(validcall) + for item in dummy: + validcall += item.split('-') + if text in validcall: + return + return ValidationError(_("Call name must be the given name that " "is normally used.")) def _setup_fields(self): @@ -811,6 +827,14 @@ class EditPerson(EditPrimary): EditName(self.dbstate, self.uistate, self.track, self.pname, self._update_name) + def _mult_surn_clicked(self, obj): + """ + Show the list entry of multiple surnames + """ + self.singsurnfr.hide_all() + self.singlesurn_active = False + self.multsurnfr.show_all() + def _update_name(self, name): """ Called when the primary name has been changed by the EditName @@ -819,9 +843,15 @@ class EditPerson(EditPrimary): This allows us to update the main form in response to any changes. """ - for obj in (self.prefix_suffix, self.patro_title, self.given, - self.ntype_field, self.surname_field, self.call): + for obj in (self.ntype_field, self.given, self.call, self.title, + self.suffix, self.nick, self.surname_field, self.prefix, + self.ortype_field): obj.update() + if len(self.obj.get_primary_name().get_surname_list()) > 1: + #TODO: multiple surname must be activated if not yet the case + print 'person editor TODO' + #TODO: update list of surnames + print 'person editor TODO 2' def load_person_image(self): """ diff --git a/src/gui/editors/editsecondary.py b/src/gui/editors/editsecondary.py index 0126f22f6..cae0401ad 100644 --- a/src/gui/editors/editsecondary.py +++ b/src/gui/editors/editsecondary.py @@ -32,6 +32,7 @@ class EditSecondary(ManagedWindow.ManagedWindow, DbGUIElement): """Create an edit window. Associates a person with the window.""" self.obj = obj + self.old_obj = obj.serialize() self.dbstate = state self.uistate = uistate self.db = state.db @@ -113,13 +114,20 @@ class EditSecondary(ManagedWindow.ManagedWindow, DbGUIElement): button.set_sensitive(not self.db.readonly) def define_cancel_button(self,button): - button.connect('clicked',self.close) + button.connect('clicked', self.canceledits) def define_help_button(self, button, webpage='', section=''): button.connect('clicked', lambda x: GrampsDisplay.help(webpage, section)) - def close(self,*obj): + def canceledits(self, *obj): + """ + Undo the edits that happened on this secondary object + """ + self.obj.unserialize(self.old_obj) + self.close(obj) + + def close(self, *obj): self._cleanup_db_connects() self._cleanup_on_exit() ManagedWindow.ManagedWindow.close(self) From 4e595d0cf311b75beabeb394c6851b5f25add49d Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 16 Oct 2010 08:06:14 +0000 Subject: [PATCH 15/28] improve tab movement on surname table svn: r16002 --- src/glade/editperson.glade | 3 +- src/gui/editors/displaytabs/surnametab.py | 35 ++++++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/glade/editperson.glade b/src/glade/editperson.glade index 73d660b24..ffe90f769 100644 --- a/src/glade/editperson.glade +++ b/src/glade/editperson.glade @@ -195,6 +195,8 @@ True True + True + True The person's given names @@ -802,7 +804,6 @@ True True True - True False Accept changes and close window True diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py index e2510487f..516ffb2d4 100644 --- a/src/gui/editors/displaytabs/surnametab.py +++ b/src/gui/editors/displaytabs/surnametab.py @@ -77,6 +77,7 @@ class SurnameTab(EmbeddedList): self.obj = name self.curr_col = -1 self.curr_cellr = None + self.curr_celle = None EmbeddedList.__init__(self, dbstate, uistate, track, _('Family Surnames'), SurnameModel, move_buttons=True) @@ -147,9 +148,10 @@ class SurnameTab(EmbeddedList): self.model.remove(node) self.update() - def edit_start(self, cellr, obj, path, colnr): + def edit_start(self, cellr, celle, path, colnr): self.curr_col = colnr self.curr_cellr = cellr + self.curr_celle = celle def edit_inline(self, cell, path, new_text, colnr): node = self.model.get_iter(path) @@ -178,8 +180,6 @@ class SurnameTab(EmbeddedList): self.prev_cell() else: return - elif event.type == gtk.gdk.KEY_PRESS and event.keyval in (_ENTER,): - self.next_cell() else: return return True @@ -187,8 +187,7 @@ class SurnameTab(EmbeddedList): def next_cell(self): """ Move to the next cell to edit it - """ - print 'captured tab' + """ (model, node) = self.selection.get_selected() if node: path = self.model.get_path(node) @@ -208,7 +207,29 @@ class SurnameTab(EmbeddedList): start_editing=True) else: #stop editing - self.curr_cellr.stop_editing(True) + self.curr_celle.editing_done() def prev_cell(self): - print 'captured tab prev' + """ + Move to the next cell to edit it + """ + (model, node) = self.selection.get_selected() + if node: + path = self.model.get_path(node) + if self.curr_col > 0: + self.tree.set_cursor_on_cell(path, + focus_column=self.columns[self.curr_col-1], + focus_cell=None, + start_editing=True) + elif self.curr_col == 0: + #go to prev line if there is one + if path[0] > 0: + newpath = (path[0]-1,) + self.selection.select_path(newpath) + self.tree.set_cursor_on_cell(newpath, + focus_column=self.columns[-1], + focus_cell=None, + start_editing=True) + else: + #stop editing + self.curr_celle.editing_done() From ada8c3095570a6dfb584c09b7f38d60afbbc7593 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 18 Oct 2010 13:32:19 +0000 Subject: [PATCH 16/28] usibility tweak on surname columns svn: r16008 --- src/gui/editors/displaytabs/surnametab.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py index 516ffb2d4..25b82dd89 100644 --- a/src/gui/editors/displaytabs/surnametab.py +++ b/src/gui/editors/displaytabs/surnametab.py @@ -93,6 +93,8 @@ class SurnameTab(EmbeddedList): renderer.set_property('editable', not self.dbstate.db.readonly) renderer.connect('editing_started', self.edit_start, colno) renderer.connect('edited', self.edit_inline, colno) + #no sorting + self.columns[colno].set_sort_column_id(-1) # now we add the two special columns # TODO @@ -175,14 +177,14 @@ class SurnameTab(EmbeddedList): if not EmbeddedList.key_pressed(self, obj, event): if event.type == gtk.gdk.KEY_PRESS and event.keyval in (_TAB,): if event.state not in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK): - self.next_cell() + return self.next_cell() elif event.state in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK): - self.prev_cell() + return self.prev_cell() else: return else: return - return True + return True def next_cell(self): """ @@ -208,6 +210,9 @@ class SurnameTab(EmbeddedList): else: #stop editing self.curr_celle.editing_done() + return + return True + def prev_cell(self): """ @@ -233,3 +238,5 @@ class SurnameTab(EmbeddedList): else: #stop editing self.curr_celle.editing_done() + return + return True From 3cfbd2482d7341c0018cc7536e9d7d06e0752f49 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 18 Oct 2010 15:49:39 +0000 Subject: [PATCH 17/28] Add origin type selector with autocompletion to surname table svn: r16009 --- src/gui/editors/displaytabs/surnamemodel.py | 4 +- src/gui/editors/displaytabs/surnametab.py | 91 ++++++++++++++++++--- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/gui/editors/displaytabs/surnamemodel.py b/src/gui/editors/displaytabs/surnamemodel.py index 2c2ff64fc..1067b040d 100644 --- a/src/gui/editors/displaytabs/surnamemodel.py +++ b/src/gui/editors/displaytabs/surnamemodel.py @@ -45,11 +45,11 @@ class SurnameModel(gtk.ListStore): def __init__(self, surn_list, db): #setup model for the treeview - gtk.ListStore.__init__(self, str, str, str, gobject.TYPE_PYOBJECT, + gtk.ListStore.__init__(self, str, str, str, str, bool, object) for surn in surn_list: # fill the liststore self.append(row=[surn.get_prefix(), surn.get_surname(), - surn.get_connector(), surn.get_origintype(), + surn.get_connector(), str(surn.get_origintype()), surn.get_primary(), surn]) self.db = db diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py index 25b82dd89..5999d338d 100644 --- a/src/gui/editors/displaytabs/surnametab.py +++ b/src/gui/editors/displaytabs/surnametab.py @@ -27,6 +27,7 @@ # #------------------------------------------------------------------------- from gen.ggettext import gettext as _ +import locale #------------------------------------------------------------------------- # @@ -34,6 +35,8 @@ from gen.ggettext import gettext as _ # #------------------------------------------------------------------------- import gtk +import gobject +import pango _TAB = gtk.gdk.keyval_from_name("Tab") _ENTER = gtk.gdk.keyval_from_name("Enter") @@ -45,6 +48,7 @@ _ENTER = gtk.gdk.keyval_from_name("Enter") from surnamemodel import SurnameModel from embeddedlist import EmbeddedList from DdTargets import DdTargets +import AutoComp from gen.lib import Surname, NameOriginType #------------------------------------------------------------------------- @@ -68,10 +72,11 @@ class SurnameTab(EmbeddedList): #index = column in model. Value = # (name, sortcol in model, width, markup/text _column_names = [ - (_('Prefix'), 0, 150, 0, -1), - (_('Surname'), 1, 250, 0, -1), - (_('Connector'), 2, 100, 0, -1), + (_('Prefix'), -1, 150, 0, -1), + (_('Surname'), -1, 250, 0, -1), + (_('Connector'), -1, 100, 0, -1), ] + _column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol def __init__(self, dbstate, uistate, track, name): self.obj = name @@ -91,14 +96,53 @@ class SurnameTab(EmbeddedList): for colno in range(len(self.columns)): for renderer in self.columns[colno].get_cell_renderers(): renderer.set_property('editable', not self.dbstate.db.readonly) - renderer.connect('editing_started', self.edit_start, colno) - renderer.connect('edited', self.edit_inline, colno) - #no sorting - self.columns[colno].set_sort_column_id(-1) + renderer.connect('editing_started', self.on_edit_start, colno) + renderer.connect('edited', self.on_edit_inline, colno) # now we add the two special columns + # combobox for type + colno = len(self.columns) + name = self._column_combo[0] + renderer = gtk.CellRendererCombo() + renderer.set_property('ellipsize', pango.ELLIPSIZE_END) + # set up the comboentry editable + no = NameOriginType() + self.cmborig = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING) + self.cmborigmap = no.get_map().copy() + keys = sorted(self.cmborigmap, self.by_value) + for key in keys: + if key != no.get_custom(): + self.cmborig.append(row=[key, self.cmborigmap[key]]) + additional = self.dbstate.db.get_origin_types() + if additional: + for type in additional: + if type: + self.cmborig.append(row=[no.get_custom(), type]) + renderer.set_property("model", self.cmborig) + renderer.set_property("text-column", 1) + renderer.set_property('editable', not self.dbstate.db.readonly) + + renderer.connect('editing_started', self.on_edit_start_cmb, colno) + renderer.connect('edited', self.on_orig_edited, colno) + # add to treeview + column = gtk.TreeViewColumn(name, renderer, text=self._column_combo[3]) + column.set_resizable(True) + column.set_sort_column_id(self._column_combo[1]) + column.set_min_width(self._column_combo[2]) + self.columns.append(column) + self.tree.append_column(column) + # toggle box for primary + colno += 1 # TODO + def by_value(self, first, second): + """ + Method for sorting keys based on the values. + """ + fvalue = self.cmborigmap[first] + svalue = self.cmborigmap[second] + return locale.strcoll(fvalue, svalue) + def get_data(self): return self.obj.get_surname_list() @@ -117,6 +161,7 @@ class SurnameTab(EmbeddedList): surn.set_prefix(unicode(self.model.get_value(node, 0))) surn.set_surname(unicode(self.model.get_value(node, 1))) surn.set_connector(unicode(self.model.get_value(node, 2))) + surn.get_origintype().set(unicode(self.model.get_value(node, 3))) surn.set_primary(self.model.get_value(node, 4)) new_list += [surn] return new_list @@ -150,16 +195,40 @@ class SurnameTab(EmbeddedList): self.model.remove(node) self.update() - def edit_start(self, cellr, celle, path, colnr): + def on_edit_start(self, cellr, celle, path, colnr): self.curr_col = colnr self.curr_cellr = cellr self.curr_celle = celle - - def edit_inline(self, cell, path, new_text, colnr): + + + def on_edit_start_cmb(self, cellr, celle, path, colnr): + self.on_edit_start(cellr, celle, path, colnr) + #set up autocomplete + completion = gtk.EntryCompletion() + completion.set_model(self.cmborig) + completion.set_minimum_key_length(1) + completion.set_text_column(1) + celle.child.set_completion(completion) + # + celle.connect('changed', self.on_origcmb_change, path, colnr) + + def on_edit_inline(self, cell, path, new_text, colnr): node = self.model.get_iter(path) self.model.set_value(node, colnr, new_text) self.update() - + + def on_orig_edited(self, cellr, path, new_text, colnr): + self.on_edit_inline(cellr, path, new_text, colnr) + + def on_origcmb_change(self, cmb, path, colnr): + act = cmb.get_active() + if act == -1: + return + self.on_orig_edited(None, path, + self.cmborig.get_value( + self.cmborig.get_iter((act,)),1), + colnr) + def edit_button_clicked(self, obj): (model, node) = self.selection.get_selected() if node: From ac5693c97f8fa08c92d6cdec47fc4414fd2a7bb2 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 18 Oct 2010 18:33:18 +0000 Subject: [PATCH 18/28] add toggle btn for primary to surname table svn: r16010 --- src/gui/editors/displaytabs/surnametab.py | 101 ++++++++++++++++++---- 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py index 5999d338d..2b259a281 100644 --- a/src/gui/editors/displaytabs/surnametab.py +++ b/src/gui/editors/displaytabs/surnametab.py @@ -77,6 +77,7 @@ class SurnameTab(EmbeddedList): (_('Connector'), -1, 100, 0, -1), ] _column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol + _column_toggle = (_('Primary'), -1, 25, 4) def __init__(self, dbstate, uistate, track, name): self.obj = name @@ -97,7 +98,7 @@ class SurnameTab(EmbeddedList): for renderer in self.columns[colno].get_cell_renderers(): renderer.set_property('editable', not self.dbstate.db.readonly) renderer.connect('editing_started', self.on_edit_start, colno) - renderer.connect('edited', self.on_edit_inline, colno) + renderer.connect('edited', self.on_edit_inline, self.column_order()[colno][1]) # now we add the two special columns # combobox for type @@ -123,7 +124,7 @@ class SurnameTab(EmbeddedList): renderer.set_property('editable', not self.dbstate.db.readonly) renderer.connect('editing_started', self.on_edit_start_cmb, colno) - renderer.connect('edited', self.on_orig_edited, colno) + renderer.connect('edited', self.on_orig_edited, self._column_combo[3]) # add to treeview column = gtk.TreeViewColumn(name, renderer, text=self._column_combo[3]) column.set_resizable(True) @@ -133,7 +134,18 @@ class SurnameTab(EmbeddedList): self.tree.append_column(column) # toggle box for primary colno += 1 - # TODO + name = self._column_toggle[0] + renderer = gtk.CellRendererToggle() + renderer.set_property('activatable', True) + renderer.set_property('radio', True) + renderer.connect( 'toggled', self.on_prim_toggled, self._column_toggle[3]) + # add to treeview + column = gtk.TreeViewColumn(name, renderer, active=self._column_toggle[3]) + column.set_resizable(True) + column.set_sort_column_id(self._column_toggle[1]) + column.set_min_width(self._column_toggle[2]) + self.columns.append(column) + self.tree.append_column(column) def by_value(self, first, second): """ @@ -167,6 +179,9 @@ class SurnameTab(EmbeddedList): return new_list def update(self): + """ + Store the present data in the model to the name object + """ new_map = self._get_surn_from_model() self.obj.set_surname_list(new_map) # update name in title name editor @@ -177,6 +192,7 @@ class SurnameTab(EmbeddedList): return ((1, 0), (1, 1), (1, 2)) def add_button_clicked(self, obj): + """Add button is clicked, add a surname to the person""" prim = False if len(self.obj.get_surname_list()) == 0: prim = true @@ -190,18 +206,29 @@ class SurnameTab(EmbeddedList): start_editing=True) def del_button_clicked(self, obj): + """ + Delete button is clicked. Remove from the model + """ (model, node) = self.selection.get_selected() if node: self.model.remove(node) self.update() def on_edit_start(self, cellr, celle, path, colnr): + """ start of editing. Store stuff so we know when editing ends where we + are + """ self.curr_col = colnr self.curr_cellr = cellr self.curr_celle = celle - def on_edit_start_cmb(self, cellr, celle, path, colnr): + """ + An edit starts in the origin type column + This means a cmb has been created as celle, and we can set up the stuff + we want this cmb to contain: autocompletion, stop edit when selection + in the cmb happens. + """ self.on_edit_start(cellr, celle, path, colnr) #set up autocomplete completion = gtk.EntryCompletion() @@ -212,15 +239,35 @@ class SurnameTab(EmbeddedList): # celle.connect('changed', self.on_origcmb_change, path, colnr) + def on_edit_start_toggle(self, cellr, celle, path, colnr): + """ + Edit + """ + self.on_edit_start(cellr, celle, path, colnr) + def on_edit_inline(self, cell, path, new_text, colnr): + """ + Edit is happening. The model is updated and the surname objects updated. + colnr must be the column in the model. + """ node = self.model.get_iter(path) self.model.set_value(node, colnr, new_text) self.update() def on_orig_edited(self, cellr, path, new_text, colnr): + """ + An edit is finished in the origin type column. For a cmb in an editor, + the model may only be updated when typing is finished, as editing stops + automatically on update of the model. + colnr must be the column in the model. + """ self.on_edit_inline(cellr, path, new_text, colnr) def on_origcmb_change(self, cmb, path, colnr): + """ + A selection occured in the cmb of the origin type column. colnr must + be the column in the model. + """ act = cmb.get_active() if act == -1: return @@ -229,7 +276,30 @@ class SurnameTab(EmbeddedList): self.cmborig.get_iter((act,)),1), colnr) + def on_prim_toggled(self, cell, path, colnr): + """ + Primary surname on path is toggled. colnr must be the col + in the model + """ + #obtain current value + node = self.model.get_iter(path) + old_val = self.model.get_value(node, colnr) + for nr in range(len(self.obj.get_surname_list())): + if nr == int(path[0]): + if old_val: + #True remains True + break + else: + #This value becomes True + self.model.set_value(self.model.get_iter((nr,)), colnr, True) + else: + self.model.set_value(self.model.get_iter((nr,)), colnr, False) + self.update() + return + def edit_button_clicked(self, obj): + """ Edit button clicked + """ (model, node) = self.selection.get_selected() if node: path = self.model.get_path(node) @@ -241,7 +311,7 @@ class SurnameTab(EmbeddedList): def key_pressed(self, obj, event): """ Handles the key being pressed. - Here we make sure tab moves to next value in row + Here we make sure tab moves to next or previous value in row on TAB """ if not EmbeddedList.key_pressed(self, obj, event): if event.type == gtk.gdk.KEY_PRESS and event.keyval in (_TAB,): @@ -261,16 +331,17 @@ class SurnameTab(EmbeddedList): """ (model, node) = self.selection.get_selected() if node: - path = self.model.get_path(node) - if self.curr_col+1 < len(self.columns): + path = int(self.model.get_path(node)[0]) + nccol = self.curr_col+1 + if nccol < 4: self.tree.set_cursor_on_cell(path, - focus_column=self.columns[self.curr_col+1], + focus_column=self.columns[nccol], focus_cell=None, start_editing=True) - elif self.curr_col+1 == len(self.columns): + elif nccol == 4: #go to next line if there is one - if path[0]+1 < len(self.obj.get_surname_list()): - newpath = (path[0]+1,) + if path < len(self.obj.get_surname_list()): + newpath = (path+1,) self.selection.select_path(newpath) self.tree.set_cursor_on_cell(newpath, focus_column=self.columns[0], @@ -289,7 +360,7 @@ class SurnameTab(EmbeddedList): """ (model, node) = self.selection.get_selected() if node: - path = self.model.get_path(node) + path = int(self.model.get_path(node)[0]) if self.curr_col > 0: self.tree.set_cursor_on_cell(path, focus_column=self.columns[self.curr_col-1], @@ -297,11 +368,11 @@ class SurnameTab(EmbeddedList): start_editing=True) elif self.curr_col == 0: #go to prev line if there is one - if path[0] > 0: - newpath = (path[0]-1,) + if path > 0: + newpath = (path-1,) self.selection.select_path(newpath) self.tree.set_cursor_on_cell(newpath, - focus_column=self.columns[-1], + focus_column=self.columns[-2], focus_cell=None, start_editing=True) else: From 7b66d246b8e443fa7d82bab507aebfb84861e980 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Mon, 18 Oct 2010 18:44:49 +0000 Subject: [PATCH 19/28] improvement in GUI on expand of displaytabs svn: r16011 --- src/gui/editors/displaytabs/embeddedlist.py | 1 + src/gui/editors/displaytabs/surnametab.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/editors/displaytabs/embeddedlist.py b/src/gui/editors/displaytabs/embeddedlist.py index 1850cbfde..6f7efe60f 100644 --- a/src/gui/editors/displaytabs/embeddedlist.py +++ b/src/gui/editors/displaytabs/embeddedlist.py @@ -443,6 +443,7 @@ class EmbeddedList(ButtonTab): # insert the colum into the tree column.set_resizable(True) column.set_clickable(True) + column.set_expand(True) column.set_min_width(self._column_names[pair[1]][2]) column.set_sort_column_id(self._column_names[pair[1]][1]) self.columns.append(column) diff --git a/src/gui/editors/displaytabs/surnametab.py b/src/gui/editors/displaytabs/surnametab.py index 2b259a281..cefa075f3 100644 --- a/src/gui/editors/displaytabs/surnametab.py +++ b/src/gui/editors/displaytabs/surnametab.py @@ -77,7 +77,7 @@ class SurnameTab(EmbeddedList): (_('Connector'), -1, 100, 0, -1), ] _column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol - _column_toggle = (_('Primary'), -1, 25, 4) + _column_toggle = (_('Primary'), -1, 80, 4) def __init__(self, dbstate, uistate, track, name): self.obj = name @@ -130,6 +130,7 @@ class SurnameTab(EmbeddedList): column.set_resizable(True) column.set_sort_column_id(self._column_combo[1]) column.set_min_width(self._column_combo[2]) + column.set_expand(True) self.columns.append(column) self.tree.append_column(column) # toggle box for primary @@ -141,7 +142,9 @@ class SurnameTab(EmbeddedList): renderer.connect( 'toggled', self.on_prim_toggled, self._column_toggle[3]) # add to treeview column = gtk.TreeViewColumn(name, renderer, active=self._column_toggle[3]) - column.set_resizable(True) + column.set_resizable(False) + column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + column.set_alignment(0.5) column.set_sort_column_id(self._column_toggle[1]) column.set_min_width(self._column_toggle[2]) self.columns.append(column) From 7400976c965c95032d1358267004d90dcbbc0793 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Wed, 20 Oct 2010 19:22:40 +0000 Subject: [PATCH 20/28] fix filters for name search svn: r16016 --- src/Filters/Rules/Person/_HasNameOf.py | 76 ++++++++++++++------ src/Filters/Rules/Person/_IncompleteNames.py | 7 +- src/Filters/Rules/Person/_RegExpName.py | 4 +- src/Filters/Rules/Person/_SearchName.py | 7 +- 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/Filters/Rules/Person/_HasNameOf.py b/src/Filters/Rules/Person/_HasNameOf.py index 02308fd5a..bc4d79a3b 100644 --- a/src/Filters/Rules/Person/_HasNameOf.py +++ b/src/Filters/Rules/Person/_HasNameOf.py @@ -34,6 +34,7 @@ from gen.ggettext import sgettext as _ # #------------------------------------------------------------------------- from Filters.Rules._Rule import Rule +from gen.lib import NameOriginType #------------------------------------------------------------------------- # @@ -43,41 +44,76 @@ from Filters.Rules._Rule import Rule class HasNameOf(Rule): """Rule that checks for full or partial name matches""" - labels = [ _('Given name:'), - _('Family name:'), - _('Suffix:'), + labels = [ _('Given name:'), + _('Full Family name:'), _('person|Title:'), + _('Suffix:'), + _('Call Name:'), + _('Nick Name:'), _('Prefix:'), + _('Single Surname:'), + _('Connector'), _('Patronymic:'), - _('Call Name:'),] + _('Family Nick Name:')] name = _('People with the ') description = _("Matches people with a specified (partial) name") category = _('General filters') - def apply(self,db,person): - self.firstn = self.list[0] - self.lastn = self.list[1] - self.surn = self.list[2] - self.title = self.list[3] - self.prefix = self.list[4] - self.patr = self.list[5] - self.calln = self.list[6] + def prepare(self, db): + self.firstn = self.list[0].upper() + self.lastn = self.list[1].upper() + self.title = self.list[2].upper() + self.suffix = self.list[3].upper() + self.calln = self.list[4].upper() + self.nick = self.list[5].upper() + self.famnick = self.list[10].upper() + #surname parts + self.prefix = self.list[6].upper() + self.surn = self.list[7].upper() + self.con = self.list[8].upper() + self.patr = self.list[9].upper() + + def apply(self, db, person): for name in [person.get_primary_name()] + person.get_alternate_names(): val = 1 - if self.firstn and name.get_first_name().upper().find(self.firstn.upper()) == -1: + valpref = 0 + if not self.prefix: + valpref = 1 + valsurn = 0 + if not self.surn: + valsurn = 1 + valcon = 0 + if not self.con: + valcon = 1 + valpatr = 0 + if not self.patr: + valpatr = 1 + if self.firstn and name.get_first_name().upper().find(self.firstn) == -1: val = 0 - if self.lastn and name.get_surname().upper().find(self.lastn.upper()) == -1: + elif self.lastn and name.get_surname().upper().find(self.lastn) == -1: val = 0 - if self.surn and name.get_suffix().upper().find(self.surn.upper()) == -1: + elif self.suffix and name.get_suffix().upper().find(self.surn) == -1: val = 0 - if self.title and name.get_title().upper().find(self.title.upper()) == -1: + elif self.title and name.get_title().upper().find(self.title) == -1: val = 0 - if self.prefix and name.get_prefix().upper().find(self.prefix.upper()) == -1: + elif self.calln and name.get_call_name().upper().find(self.calln) == -1: val = 0 - if self.patr and name.get_patronymic().upper().find(self.patr.upper()) == -1: + elif self.nick and name.get_nick_name().upper().find(self.nick) == -1: val = 0 - if self.calln and name.get_call_name().upper().find(self.calln.upper()) == -1: + elif self.famnick and name.get_family_nick_name().upper().find(self.famnick) == -1: val = 0 - if val == 1: + else: + #obtain surnames + for surn in name.get_surname_list(): + if self.prefix and surn.get_prefix().upper().find(self.prefix) != -1: + valpref = 1 + if self.surn and surn.get_surname().upper().find(self.surn) != -1: + valsurn = 1 + if self.con and surn.get_connector().upper().find(self.con) != -1: + valcon = 1 + if self.patr and surn.get_origintype().value == NameOriginType.PATRONYMIC \ + and surn.get_surname().upper().find(self.patr) != -1: + valpatr = 1 + if val == 1 and valpref == 1 and valsurn == 1 and valcon == 1 and valpatr ==1: return True return False diff --git a/src/Filters/Rules/Person/_IncompleteNames.py b/src/Filters/Rules/Person/_IncompleteNames.py index 7fed919e3..a0912fcbd 100644 --- a/src/Filters/Rules/Person/_IncompleteNames.py +++ b/src/Filters/Rules/Person/_IncompleteNames.py @@ -48,8 +48,9 @@ class IncompleteNames(Rule): def apply(self,db,person): for name in [person.get_primary_name()] + person.get_alternate_names(): - if name.get_first_name() == "": - return True - if name.get_surname() == "": + if name.get_first_name().strip() == "": return True + for surn in name.get_surname_list(): + if surn.get_surname().strip() == "": + return True return False diff --git a/src/Filters/Rules/Person/_RegExpName.py b/src/Filters/Rules/Person/_RegExpName.py index 5a0cde75a..a39f76481 100644 --- a/src/Filters/Rules/Person/_RegExpName.py +++ b/src/Filters/Rules/Person/_RegExpName.py @@ -59,8 +59,8 @@ class RegExpName(Rule): def apply(self,db,person): for name in [person.get_primary_name()] + person.get_alternate_names(): - for field in [name.first_name, name.surname, name.suffix, name.title, - name.prefix, name.patronymic, name.call]: + for field in [name.first_name, name.get_surname(), name.suffix, + name.title, name.nick, name.famnick, self.call]: if self.match.match(field): return True else: diff --git a/src/Filters/Rules/Person/_SearchName.py b/src/Filters/Rules/Person/_SearchName.py index 99e7b74eb..678eab7bd 100644 --- a/src/Filters/Rules/Person/_SearchName.py +++ b/src/Filters/Rules/Person/_SearchName.py @@ -49,12 +49,13 @@ class SearchName(Rule): category = _('General filters') def apply(self, db, person): - src = self.list[0].upper() + if not src: + return False for name in [person.get_primary_name()] + person.get_alternate_names(): - for field in [name.first_name, name.surname, name.suffix, name.title, - name.prefix, name.patronymic, name.call]: + for field in [name.first_name, name.get_surname(), name.suffix, + name.title, name.nick, name.famnick, self.call]: if src and field.upper().find(src) != -1: return True else: From 4d89c9da3bf5d7cdd8a12c9e722cbd656206ef34 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Wed, 20 Oct 2010 19:50:00 +0000 Subject: [PATCH 21/28] fix name capitilization tool for surnames svn: r16017 --- src/plugins/tool/ChangeNames.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/tool/ChangeNames.py b/src/plugins/tool/ChangeNames.py index 8cd1521f8..7226bbf42 100644 --- a/src/plugins/tool/ChangeNames.py +++ b/src/plugins/tool/ChangeNames.py @@ -37,6 +37,7 @@ import gtk # gramps modules # #------------------------------------------------------------------------- +from gen.db import find_surname_name import const from gui.utils import ProgressMeter import GrampsDisplay @@ -243,11 +244,12 @@ class ChangeNames(tool.BatchTool, ManagedWindow.ManagedWindow): #person = Person(data) change = False for name in [person.get_primary_name()] + person.get_alternate_names(): - sname = name.get_surname() + sname = find_surname_name(handle, name.serialize()) if sname in changelist: change = True - sname = self.name_cap(sname) - name.set_surname(sname) + for surn in name.get_surname_list(): + sname = self.name_cap(surn.get_surname()) + surn.set_surname(sname) if change: #cursor.update(handle, person.serialize()) self.db.commit_person(person, transaction=self.trans) From 460497f489a3b966946c7dcc5dff0b503e55688e Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Wed, 20 Oct 2010 20:06:36 +0000 Subject: [PATCH 22/28] fix error in changed filters svn: r16018 --- src/Filters/Rules/Person/_RegExpName.py | 2 +- src/Filters/Rules/Person/_SearchName.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Filters/Rules/Person/_RegExpName.py b/src/Filters/Rules/Person/_RegExpName.py index a39f76481..226c61e5b 100644 --- a/src/Filters/Rules/Person/_RegExpName.py +++ b/src/Filters/Rules/Person/_RegExpName.py @@ -60,7 +60,7 @@ class RegExpName(Rule): def apply(self,db,person): for name in [person.get_primary_name()] + person.get_alternate_names(): for field in [name.first_name, name.get_surname(), name.suffix, - name.title, name.nick, name.famnick, self.call]: + name.title, name.nick, name.famnick, name.call]: if self.match.match(field): return True else: diff --git a/src/Filters/Rules/Person/_SearchName.py b/src/Filters/Rules/Person/_SearchName.py index 678eab7bd..6d84a3186 100644 --- a/src/Filters/Rules/Person/_SearchName.py +++ b/src/Filters/Rules/Person/_SearchName.py @@ -55,7 +55,7 @@ class SearchName(Rule): for name in [person.get_primary_name()] + person.get_alternate_names(): for field in [name.first_name, name.get_surname(), name.suffix, - name.title, name.nick, name.famnick, self.call]: + name.title, name.nick, name.famnick, name.call]: if src and field.upper().find(src) != -1: return True else: From a3556d839bd580e8564c87595a32e0877d734038 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Wed, 20 Oct 2010 20:07:05 +0000 Subject: [PATCH 23/28] fix the finddupes tool for surnames svn: r16019 --- src/plugins/tool/FindDupes.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/plugins/tool/FindDupes.py b/src/plugins/tool/FindDupes.py index a28015bb0..85f394335 100644 --- a/src/plugins/tool/FindDupes.py +++ b/src/plugins/tool/FindDupes.py @@ -144,7 +144,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC) - def ancestors_of(self,p1_id,id_list): + def ancestors_of(self, p1_id, id_list): if (not p1_id) or (p1_id in id_list): return id_list.append(p1_id) @@ -181,7 +181,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): except Errors.WindowActiveError: pass - def find_potentials(self,thresh): + def find_potentials(self, thresh): self.progress = ProgressMeter(_('Find Duplicates'), _('Looking for duplicate people')) @@ -197,7 +197,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): for p1_id in self.db.iter_person_handles(): self.progress.step() p1 = self.db.get_person_from_handle(p1_id) - key = self.gen_key(p1.get_primary_name().get_surname()) + key = self.gen_key(get_surnames(p1.get_primary_name())) if p1.get_gender() == gen.lib.Person.MALE: if key in males: males[key].append(p1_id) @@ -216,7 +216,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): self.progress.step() p1 = self.db.get_person_from_handle(p1key) - key = self.gen_key(p1.get_primary_name().get_surname()) + key = self.gen_key(get_surnames(p1.get_primary_name())) if p1.get_gender() == gen.lib.Person.MALE: remaining = males[key] else: @@ -246,7 +246,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): self.length = len(self.list) self.progress.close() - def gen_key(self,val): + def gen_key(self, val): if self.use_soundex: try: return soundex.soundex(val) @@ -255,7 +255,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): else: return val - def compare_people(self,p1,p2): + def compare_people(self, p1, p2): name1 = p1.get_primary_name() name2 = p2.get_primary_name() @@ -397,7 +397,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): chance += value return chance - def name_compare(self,s1,s2): + def name_compare(self, s1, s2): if self.use_soundex: try: return soundex.compare(s1,s2) @@ -406,7 +406,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): else: return s1 == s2 - def date_match(self,date1,date2): + def date_match(self, date1, date2): if date1.is_empty() or date2.is_empty(): return 0 if date1.is_equal(date2): @@ -425,7 +425,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): else: return -1 - def range_compare(self,date1,date2): + def range_compare(self, date1, date2): start_date_1 = date1.get_start_date()[0:3] start_date_2 = date2.get_start_date()[0:3] stop_date_1 = date1.get_stop_date()[0:3] @@ -454,9 +454,9 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): if not name1 or not name: return 0 - srn1 = name.get_surname() + srn1 = get_surnames(name) sfx1 = name.get_suffix() - srn2 = name1.get_surname() + srn2 = get_surnames(name1) sfx2 = name1.get_suffix() if not self.name_compare(srn1,srn2): @@ -476,7 +476,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): else: return self.list_reduce(list2,list1) - def place_match(self,p1_id,p2_id): + def place_match(self, p1_id, p2_id): if p1_id == p2_id: return 1 @@ -509,7 +509,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): value += 0.25 return min(value,1) if value else -1 - def list_reduce(self,list1,list2): + def list_reduce(self, list1, list2): value = 0 for name in list1: for name2 in list2: @@ -526,7 +526,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow): class ShowMatches(ManagedWindow.ManagedWindow): - def __init__(self,dbstate,uistate,track,the_list,the_map,callback): + def __init__(self, dbstate, uistate, track, the_list, the_map, callback): ManagedWindow.ManagedWindow.__init__(self,uistate,track,self.__class__) self.dellist = {} @@ -632,7 +632,11 @@ def get_name_obj(person): return person.get_primary_name() else: return None - + +def get_surnames(name): + """Construct a full surname of the surnames""" + ' '.join([surn.get_surname() for surn in name.get_surname_list()]) + #------------------------------------------------------------------------- # # From 920535e3f6013b7ca00b5f0015c8a3ad137629de Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Wed, 20 Oct 2010 20:26:23 +0000 Subject: [PATCH 24/28] fix in name object and quickview for names svn: r16020 --- src/gen/lib/name.py | 2 +- src/gen/lib/surnamebase.py | 1 - src/plugins/quickview/SameSurnames.py | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index 73c0f10ea..9e52beac5 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.py @@ -266,7 +266,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, if self.group_as: return self.group_as else: - return self.surname + return self.get_primary_surname().get_surname() def set_sort_as(self, value): """ diff --git a/src/gen/lib/surnamebase.py b/src/gen/lib/surnamebase.py index 7b570e9e9..82186e9be 100644 --- a/src/gen/lib/surnamebase.py +++ b/src/gen/lib/surnamebase.py @@ -228,4 +228,3 @@ class SurnameBase(object): conn = surn.get_connector() if conn: connl.append(conn) - \ No newline at end of file diff --git a/src/plugins/quickview/SameSurnames.py b/src/plugins/quickview/SameSurnames.py index 96da9ada7..3aacac8ff 100644 --- a/src/plugins/quickview/SameSurnames.py +++ b/src/plugins/quickview/SameSurnames.py @@ -54,7 +54,7 @@ class SameSurname(Rule): def apply(self, db, person): src = self.list[0].upper() for name in [person.get_primary_name()] + person.get_alternate_names(): - if name.surname and name.surname.upper() == src.upper(): + if name.get_surname() and name.get_surname().upper() == src.upper(): return True return False @@ -103,7 +103,7 @@ def run(database, document, person): surname = person rsurname = person # display the title - sdoc.title(_("People with the surname '%s'") % surname) + sdoc.title(_("People sharing the surname '%s'") % surname) sdoc.paragraph("") stab.columns(_("Person"), _("Birth Date"), _("Name type")) filter = GenericFilterFactory('Person')() From ee91b33a43d907b57dc051e56c8ac2c0d12b4410 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 22 Oct 2010 14:20:01 +0000 Subject: [PATCH 25/28] gedcom import export for new name structure svn: r16022 --- src/gen/lib/name.py | 36 ----------------------- src/gen/lib/surname.py | 2 +- src/plugins/export/ExportGedcom.py | 33 ++++++++++++--------- src/plugins/lib/libgedcom.py | 47 +++++++++++++++++++++++------- src/plugins/tool/PatchNames.py | 6 ++-- 5 files changed, 58 insertions(+), 66 deletions(-) diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index 9e52beac5..bfa1884c0 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.py @@ -456,39 +456,3 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, return '%s /%s/' % (firstname, surname) else: return '%s /%s/ %s' % (firstname, surname, suffix) - -## -## #DEPRECATED METHODS -## -## -## def get_surname_prefix(self): -## """ -## Return the prefix (or article) of a surname. -## -## The prefix is not used for sorting or grouping. -## """ -## return self.prefix -## -## def set_surname_prefix(self, val): -## """ -## Set the prefix (or article) of a surname. -## -## Examples of articles would be 'de' or 'van'. -## """ -## self.prefix = val -## -## def get_patronymic(self): -## """Return the patronymic name for the Name instance.""" -## return self.patronymic -## -## def set_patronymic(self, name): -## """Set the patronymic name for the Name instance.""" -## self.patronymic = name -## -## def get_surname(self): -## """Return the surname (or last name) for the Name instance.""" -## return self.surname -## -## def set_surname(self, name): -## """Set the surname (or last name) for the Name instance.""" -## self.surname = name diff --git a/src/gen/lib/surname.py b/src/gen/lib/surname.py index 251f308b5..8b7232a92 100644 --- a/src/gen/lib/surname.py +++ b/src/gen/lib/surname.py @@ -185,7 +185,7 @@ class Surname(SecondaryObject): """Return if this surname is the primary surname""" return self.primary - def set_primary(self, primary): + def set_primary(self, primary=True): """Set if this surname is the primary surname.replace Use :class:`~gen.lib.surname.SurnameBase` to set the primary surname via :method:`~gen.lib.surname.SurnameBase.set_primary_surname` diff --git a/src/plugins/export/ExportGedcom.py b/src/plugins/export/ExportGedcom.py index 2ab6fb460..8354bf13d 100644 --- a/src/plugins/export/ExportGedcom.py +++ b/src/plugins/export/ExportGedcom.py @@ -498,14 +498,12 @@ class GedcomWriter(UpdateCallback): """ Write the names associated with the person to the current level. - Since nicknames are now separate from the name structure, we search - the attribute list to see if we can find a nickname. Because we do - not know the mappings, we just take the first nickname we find, and - add it to the primary name. + Since nicknames in version < 3.3 are separate from the name structure, + we search the attribute list to see if we can find a nickname. + Because we do not know the mappings, we just take the first nickname + we find, and add it to the primary name. + If a nickname is present in the name structure, it has precedence - All other names are assumed to not have a nickname, even if other - nicknames exist in the attribute list. - """ nicknames = [ attr.get_value() for attr in person.get_attribute_list() if int(attr.get_type()) == gen.lib.AttributeType.NICKNAME ] @@ -1187,7 +1185,7 @@ class GedcomWriter(UpdateCallback): elif date.get_text(): self.__writeln(level, 'DATE', date.get_text()) - def __person_name(self, name, nick): + def __person_name(self, name, attr_nick): """ n NAME {1:1} +1 NPFX {0:1} @@ -1202,13 +1200,21 @@ class GedcomWriter(UpdateCallback): gedcom_name = name.get_gedcom_name() firstname = name.get_first_name().strip() - patron = name.get_patronymic().strip() - if patron: - firstname = "%s %s" % (firstname, patron) - surname = name.get_surname().replace('/', '?') - surprefix = name.get_surname_prefix().replace('/', '?') + surns = [] + surprefs = [] + for surn in name.get_surname_list(): + surns.append(surn.get_surname().replace('/', '?')) + if surn.get_connector(): + #we store connector with the surname + surns[-1] = surns[-1] + ' ' + surn.get_connector() + surprefs.append(surn.get_prefix().replace('/', '?')) + surname = ', '.join(surns) + surprefix = ', '.join(surprefs) suffix = name.get_suffix() title = name.get_title() + nick = name.get_nick_name() + if nick.strip() == '': + nick = attr_nick self.__writeln(1, 'NAME', gedcom_name) @@ -1218,7 +1224,6 @@ class GedcomWriter(UpdateCallback): self.__writeln(2, 'SPFX', surprefix) if surname: self.__writeln(2, 'SURN', surname) - if name.get_suffix(): self.__writeln(2, 'NSFX', suffix) if name.get_title(): diff --git a/src/plugins/lib/libgedcom.py b/src/plugins/lib/libgedcom.py index ce63d88b6..09724342a 100644 --- a/src/plugins/lib/libgedcom.py +++ b/src/plugins/lib/libgedcom.py @@ -1672,16 +1672,25 @@ class GedcomParser(UpdateCallback): 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()) - name.set_surname(names[0].strip()) + surn = gen.lib.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()) - name.set_surname(names[2].strip()) + surn = gen.lib.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 @@ -2781,7 +2790,7 @@ class GedcomParser(UpdateCallback): sub_state.name = name sub_state.level = 2 - self.__parse_level(sub_state, self.name_parse_tbl, self.__undefined) + self.__parse_level(sub_state, self.name_parse_tbl, self.__undefined) def __person_object(self, line, state): """ @@ -3163,7 +3172,13 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.name.set_surname_prefix(line.data.strip()) + if state.name.get_surname_list(): + state.name.get_surname_list()[0].set_prefix(line.data.strip()) + else: + surn = gen.lib.Surname() + surn.set_prefix(line.data.strip()) + surn.set_primary() + state.name.set_surname_list([surn]) self.__skip_subordinate_levels(state.level+1) def __name_surn(self, line, state): @@ -3173,7 +3188,13 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.name.set_surname(line.data.strip()) + if state.name.get_surname_list(): + state.name.get_surname_list()[0].set_surname(line.data.strip()) + else: + surn = gen.lib.Surname() + surn.set_surname(line.data.strip()) + surn.set_primary() + state.name.set_surname_list([surn]) self.__skip_subordinate_levels(state.level+1) def __name_marnm(self, line, state): @@ -3187,7 +3208,10 @@ class GedcomParser(UpdateCallback): data = text.split() if len(data) == 1: name = gen.lib.Name(state.person.primary_name) - name.set_surname(data[0].strip()) + surn = gen.lib.Surname() + surn.set_surname(data[0].strip()) + surn.set_primary() + name.set_surname_list([surn]) name.set_type(gen.lib.NameType.MARRIED) state.person.add_alternate_name(name) elif len(data) > 1: @@ -3202,8 +3226,12 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - if state.name.get_suffix() == "": + if state.name.get_suffix() == "" or state.name.get_suffix() == line.data: + #suffix might be set before when parsing name string state.name.set_suffix(line.data) + else: + #previously set suffix different, to not loose information, append + state.name.set_suffix(state.name.get_suffix() + ' ' + line.data) self.__skip_subordinate_levels(state.level+1) def __name_nick(self, line, state): @@ -3213,10 +3241,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - attr = gen.lib.Attribute() - attr.set_type(gen.lib.AttributeType.NICKNAME) - attr.set_value(line.data) - state.person.add_attribute(attr) + state.name.set_nick_name(line.data.strip()) self.__skip_subordinate_levels(state.level+1) def __name_aka(self, line, state): diff --git a/src/plugins/tool/PatchNames.py b/src/plugins/tool/PatchNames.py index 7a667d0db..11669b119 100644 --- a/src/plugins/tool/PatchNames.py +++ b/src/plugins/tool/PatchNames.py @@ -77,6 +77,7 @@ prefix_list = [ "um", "una", "uno", "der", "ter", "te", "die", ] +connector_list = ['e', 'y', ] _title_re = re.compile(r"^ ([A-Za-z][A-Za-z]+\.) \s+ (.+) $", re.VERBOSE) _nick_re = re.compile(r"(.+) \s* [(\"] (.+) [)\"]", re.VERBOSE) @@ -308,10 +309,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): name = p.get_primary_name() name.set_first_name(grp[1].strip()) nick_name = grp[2].strip() - attr = gen.lib.Attribute() - attr.set_type(gen.lib.AttributeType.NICKNAME) - attr.set_value(nick_name) - p.add_attribute(attr) + name.set_nick_name(nick_name) self.db.commit_person(p, trans) for grp in self.title_list: From d7178e96d750dce8263e82ec2bf6c8c1bc33411d Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 23 Oct 2010 14:50:25 +0000 Subject: [PATCH 26/28] new patch names tool version 1 svn: r16028 --- src/plugins/tool/PatchNames.py | 461 +++++++++++++++++++++++---------- 1 file changed, 326 insertions(+), 135 deletions(-) diff --git a/src/plugins/tool/PatchNames.py b/src/plugins/tool/PatchNames.py index 11669b119..706f8f87c 100644 --- a/src/plugins/tool/PatchNames.py +++ b/src/plugins/tool/PatchNames.py @@ -70,25 +70,19 @@ WIKI_HELP_SEC = _('manual|Extract_Information_from_Names') # List of possible surname prefixes. Notice that you must run the tool # multiple times for prefixes such as "van der". -prefix_list = [ +PREFIX_LIST = [ "de", "van", "von", "di", "le", "du", "dela", "della", "des", "vande", "ten", "da", "af", "den", "das", "dello", "del", "en", "ein", "el" "et", "les", "lo", "los", "un", "um", "una", "uno", "der", "ter", "te", "die", ] -connector_list = ['e', 'y', ] +CONNECTOR_LIST = ['e', 'y', ] +CONNECTOR_LIST_NONSPLIT = ['de', 'van'] _title_re = re.compile(r"^ ([A-Za-z][A-Za-z]+\.) \s+ (.+) $", re.VERBOSE) _nick_re = re.compile(r"(.+) \s* [(\"] (.+) [)\"]", re.VERBOSE) -# Find a prefix in the first_name -_fn_prefix_re = re.compile("(\S+)\s+(%s)\s*$" % '|'.join(prefix_list), - re.IGNORECASE) - -# Find a prefix in the surname -_sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(prefix_list), - re.IGNORECASE) #------------------------------------------------------------------------- # @@ -102,7 +96,11 @@ _sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(prefix_list), class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): - + titleid = 1 + nickid = 2 + pref1id = 3 + compid = 4 + def __init__(self, dbstate, uistate, options_class, name, callback=None): self.label = _('Name and title extraction tool') ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__) @@ -111,12 +109,63 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): tool.BatchTool.__init__(self, dbstate, options_class, name) if self.fail: return + + winprefix = gtk.Dialog("Default prefix and connector settings", + self.uistate.window, + gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, + (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + + winprefix.set_has_separator(False) + winprefix.vbox.set_spacing(5) + hboxpref = gtk.HBox() + hboxpref.pack_start(gtk.Label(_('Prefixes to search for:')), + expand=False, padding=5) + self.prefixbox = gtk.Entry() + self.prefixbox.set_text(', '.join(PREFIX_LIST)) + hboxpref.pack_start(self.prefixbox) + winprefix.vbox.pack_start(hboxpref) + hboxcon = gtk.HBox() + hboxcon.pack_start(gtk.Label(_('Connectors splitting surnames:')), + expand=False, padding=5) + self.conbox = gtk.Entry() + self.conbox.set_text(', '.join(CONNECTOR_LIST)) + hboxcon.pack_start(self.conbox) + winprefix.vbox.pack_start(hboxcon) + hboxconns = gtk.HBox() + hboxconns.pack_start(gtk.Label(_('Connectors non-splitting surnames:')), + expand=False, padding=5) + self.connsbox = gtk.Entry() + self.connsbox.set_text(', '.join(CONNECTOR_LIST_NONSPLIT)) + hboxconns.pack_start(self.connsbox) + winprefix.vbox.pack_start(hboxconns) + winprefix.show_all() + winprefix.resize(700, 100) + + response = winprefix.run() + self.prefix_list = self.prefixbox.get_text().split(',') + self.prefix_list = map(strip, self.prefix_list) + self.prefixbox = None + self.connector_list = self.conbox.get_text().split(',') + self.connector_list = map(strip, self.connector_list) + self.conbox = None + self.connector_list_nonsplit = self.connsbox.get_text().split(',') + self.connector_list_nonsplit = map(strip, self.connector_list_nonsplit) + self.connsbox = None + + # Find a prefix in the first_name + self._fn_prefix_re = re.compile("(\S+)\s+(%s)\s*$" % '|'.join(self.prefix_list), + re.IGNORECASE) + + # Find a prefix in the surname + self._sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(self.prefix_list), + re.IGNORECASE) + # Find a connector in the surname + self._sn_con_re = re.compile("^\s*(.+)\s+(%s)\s+(.+)" % '|'.join(self.connector_list), + re.IGNORECASE) + winprefix.destroy() self.cb = callback - self.title_list = [] - self.nick_list = [] - self.prefix1_list = [] - self.prefix2_list = [] + self.handle_to_action = {} self.progress = ProgressMeter( _('Extracting Information from Names'), '') @@ -128,6 +177,18 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): name = person.get_primary_name() first = name.get_first_name() sname = name.get_surname() + + old_prefix = [] + old_surn = [] + old_con = [] + old_prim = [] + old_orig = [] + for surn in name.get_surname_list(): + old_prefix.append(surn.get_prefix()) + old_surn.append(surn.get_surname()) + old_con.append(surn.get_connector()) + old_prim.append(surn.get_primary()) + old_orig.append(surn.get_origintype()) if name.get_title(): old_title = [name.get_title()] @@ -141,46 +202,153 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): first = groups[1] new_title.append(groups[0]) match = _title_re.match(first) + matchnick = _nick_re.match(first) if new_title: - self.title_list.append((key, " ".join(old_title+new_title), - first)) - continue - - match = _nick_re.match(first) - if match: - groups = match.groups() - self.nick_list.append((key, groups[0], groups[1])) - continue - - old_prefix = name.get_surname_prefix() - - # First try to find the name prefix in the first_name - match = _fn_prefix_re.match(first) - if match: - groups = match.groups() - if old_prefix: - # Put the found prefix before the old prefix - new_prefix = " ".join([groups[1], old_prefix]) + titleval = (" ".join(old_title+new_title), first) + if key in self.handle_to_action: + self.handle_to_action[key][self.titleid] = titleval else: - new_prefix = groups[1] - self.prefix1_list.append((key, groups[0], new_prefix)) - continue - - # Next, try to find the name prefix in the surname - match = _sn_prefix_re.match(sname) - if match: - groups = match.groups() - if old_prefix: - # Put the found prefix after the old prefix - new_prefix = " ".join([old_prefix, groups[0]]) + self.handle_to_action[key] = {self.titleid: titleval} + elif matchnick: + # we check for nick, which changes given name like title + groups = matchnick.groups() + nickval = (groups[0], groups[1]) + if key in self.handle_to_action: + self.handle_to_action[key][self.nickid] = nickval else: - new_prefix = groups[0] - self.prefix2_list.append((key, groups[1], new_prefix)) + self.handle_to_action[key] = {self.nickid: nickval} + else: + # Try to find the name prefix in the given name, also this + # changes given name + match = self._fn_prefix_re.match(first) + if match: + groups = match.groups() + if old_prefix[0]: + # Put the found prefix before the old prefix + new_prefix = " ".join([groups[1], old_prefix[0]]) + else: + new_prefix = groups[1] + pref1val = (groups[0], new_prefix, groups[1]) + if key in self.handle_to_action: + self.handle_to_action[key][self.pref1id] = pref1val + else: + self.handle_to_action[key] = {self.pref1id: pref1val} + + #check for Gedcom import of compound surnames + if len(old_surn) == 1 and old_con[0] == '': + prefixes = old_prefix[0].split(',') + surnames = old_surn[0].split(',') + if len(prefixes) > 1 and len(prefixes) == len(surnames): + #assume a list of prefix and a list of surnames + prefixes = map(strip, prefixes) + surnames = map(strip, surnames) + primaries = [False] * len(prefixes) + primaries[0] = True + origs = [] + for ind in range(len(prefixes)): + origs.append(gen.lib.NameOriginType()) + origs[0] = old_orig[0] + compoundval = (surnames, prefixes, ['']*len(prefixes), + primaries, origs) + if key in self.handle_to_action: + self.handle_to_action[key][self.compid] = compoundval + else: + self.handle_to_action[key] = {self.compid: compoundval} + #we cannot check compound surnames, so continue the loop + continue + + # Next, try to split surname in compounds: prefix surname connector + found = False + new_prefix_list = [] + new_surname_list = [] + new_connector_list = [] + new_prim_list = [] + new_orig_list = [] + ind = 0 + cont = True + for pref, surn, con, prim, orig in zip(old_prefix, old_surn, + old_con, old_prim, old_orig): + while cont: + new_prefix_list.append(pref) + new_surname_list.append('') + new_connector_list.append(con) + new_prim_list.append(prim) + new_orig_list.append(orig) + surnval = surn.split() + if surnval == []: + continue + val = surnval.pop(0) + while cont and (val.lower() in self.prefix_list): + found = True + if new_prefix_list[-1]: + new_prefix_list[-1] += ' ' + val + else: + new_prefix_list[-1] = val + try: + val = surnval.pop(0) + except IndexError: + val = '' + cont = False + #after prefix we have a surname + if cont: + new_surname_list[-1] = val + try: + val = surnval.pop(0) + except IndexError: + val = '' + cont = False + #if value after surname indicates continue, then continue + if cont and ((val.lower() in self.connector_list_nonsplit) or + (new_surname_list[-1].lower() in self.connector_list_nonsplit)): + #add this val to the current surname + new_surname_list[-1] += ' ' + val + try: + val = surnval.pop(0) + except IndexError: + val = '' + cont = False + # if previous is non-splitting connector, then add new val to + # current surname + if cont and (new_surname_list[-1].lower() in self.connector_list_nonsplit): + new_surname_list[-1] += ' ' + val + try: + val = surnval.pop(0) + except IndexError: + val = '' + cont = False + #if next is a connector, add it to the surname + if cont and val.lower() in self.connector_list: + found = True + if new_connector_list[-1]: + new_connector_list[-1] = ' ' + val + else: + new_prefix_list[-1] = val + try: + val = surnval.pop(0) + except IndexError: + val = '' + cont = False + #initialize for a next surname in case there are still + #val + if cont: + found = True # we split surname + pref='' + con = '' + prim = False + orig = gen.lib.NameOriginType() + ind += 1 + if found: + compoundval = (new_surname_list, new_prefix_list, + new_connector_list, new_prim_list, new_orig_list) + if key in self.handle_to_action: + self.handle_to_action[key][self.compid] = compoundval + else: + self.handle_to_action[key] = {self.compid: compoundval} self.progress.step() - if self.nick_list or self.title_list or self.prefix1_list or self.prefix2_list: + if self.handle_to_action: self.display() else: self.progress.close() @@ -229,7 +397,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): c = gtk.TreeViewColumn(_('Value'), gtk.CellRendererText(), text=3) self.list.append_column(c) - c = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=4) + c = gtk.TreeViewColumn(_('Current Name'), gtk.CellRendererText(), text=4) self.list.append_column(c) self.list.set_model(self.model) @@ -237,58 +405,65 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): self.nick_hash = {} self.title_hash = {} self.prefix1_hash = {} - self.prefix2_hash = {} + self.compound_hash = {} self.progress.set_pass(_('Building display'), - len(self.nick_list)+len(self.title_list) - +len(self.prefix1_list)+len(self.prefix2_list)) + len(self.handle_to_action.keys())) - for (pid, name, nick) in self.nick_list: - p = self.db.get_person_from_handle(pid) + for key, data in self.handle_to_action.items(): + p = self.db.get_person_from_handle(key) gid = p.get_gramps_id() - handle = self.model.append() - self.model.set_value(handle, 0, 1) - self.model.set_value(handle, 1, gid) - self.model.set_value(handle, 2, _('Nickname')) - self.model.set_value(handle, 3, nick) - self.model.set_value(handle, 4, p.get_primary_name().get_name()) - self.nick_hash[pid] = handle - self.progress.step() - - for (pid, title, name) in self.title_list: - p = self.db.get_person_from_handle(pid) - gid = p.get_gramps_id() - handle = self.model.append() - self.model.set_value(handle, 0, 1) - self.model.set_value(handle, 1, gid) - self.model.set_value(handle, 2, _('Person|Title')) - self.model.set_value(handle, 3, title) - self.model.set_value(handle, 4, p.get_primary_name().get_name()) - self.title_hash[pid] = handle - self.progress.step() - - for (pid, fname, prefix) in self.prefix1_list: - p = self.db.get_person_from_handle(pid) - gid = p.get_gramps_id() - handle = self.model.append() - self.model.set_value(handle, 0, 1) - self.model.set_value(handle, 1, gid) - self.model.set_value(handle, 2, _('Prefix')) - self.model.set_value(handle, 3, prefix) - self.model.set_value(handle, 4, p.get_primary_name().get_name()) - self.prefix1_hash[pid] = handle - self.progress.step() - - for (pid, sname, prefix) in self.prefix2_list: - p = self.db.get_person_from_handle(pid) - gid = p.get_gramps_id() - handle = self.model.append() - self.model.set_value(handle, 0, 1) - self.model.set_value(handle, 1, gid) - self.model.set_value(handle, 2, _('Prefix')) - self.model.set_value(handle, 3, prefix) - self.model.set_value(handle, 4, p.get_primary_name().get_name()) - self.prefix2_hash[pid] = handle + if self.nickid in data: + given, nick = data[self.nickid] + handle = self.model.append() + self.model.set_value(handle, 0, 1) + self.model.set_value(handle, 1, gid) + self.model.set_value(handle, 2, _('Nickname')) + self.model.set_value(handle, 3, nick) + self.model.set_value(handle, 4, p.get_primary_name().get_name()) + self.nick_hash[key] = handle + + if self.titleid in data: + title, given = data[self.titleid] + handle = self.model.append() + self.model.set_value(handle, 0, 1) + self.model.set_value(handle, 1, gid) + self.model.set_value(handle, 2, _('Person|Title')) + self.model.set_value(handle, 3, title) + self.model.set_value(handle, 4, p.get_primary_name().get_name()) + self.title_hash[key] = handle + + if self.pref1id in data: + given, prefixtotal, new_prefix = data[self.pref1id] + handle = self.model.append() + self.model.set_value(handle, 0, 1) + self.model.set_value(handle, 1, gid) + self.model.set_value(handle, 2, _('Prefix in given name')) + self.model.set_value(handle, 3, prefixtotal) + self.model.set_value(handle, 4, p.get_primary_name().get_name()) + self.prefix1_hash[key] = handle + + if self.compid in data: + surn_list, pref_list, con_list, prims, origs = data[self.compid] + handle = self.model.append() + self.model.set_value(handle, 0, 1) + self.model.set_value(handle, 1, gid) + self.model.set_value(handle, 2, _('Compound surname')) + newval = '' + for sur, pre, con in zip(surn_list, pref_list, con_list): + if newval: + newval += '-[' + else: + newval = '[' + newval += pre + ',' + sur + if con: + newval += ',' + con + ']' + else: + newval += ']' + self.model.set_value(handle, 3, newval) + self.model.set_value(handle, 4, p.get_primary_name().get_name()) + self.compound_hash[key] = handle + self.progress.step() self.progress.close() @@ -301,46 +476,58 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): def on_ok_clicked(self, obj): trans = self.db.transaction_begin("", batch=True) self.db.disable_signals() - for grp in self.nick_list: - handle = self.nick_hash[grp[0]] - val = self.model.get_value(handle, 0) - if val: - p = self.db.get_person_from_handle(grp[0]) - name = p.get_primary_name() - name.set_first_name(grp[1].strip()) - nick_name = grp[2].strip() - name.set_nick_name(nick_name) - self.db.commit_person(p, trans) - for grp in self.title_list: - handle = self.title_hash[grp[0]] - val = self.model.get_value(handle, 0) - if val: - p = self.db.get_person_from_handle(grp[0]) - name = p.get_primary_name() - name.set_first_name(grp[2].strip()) - name.set_title(grp[1].strip()) - self.db.commit_person(p, trans) - - for grp in self.prefix1_list: - handle = self.prefix1_hash[grp[0]] - val = self.model.get_value(handle, 0) - if val: - p = self.db.get_person_from_handle(grp[0]) - name = p.get_primary_name() - name.set_first_name(grp[1].strip()) - name.set_surname_prefix(grp[2].strip()) - self.db.commit_person(p, trans) - - for grp in self.prefix2_list: - handle = self.prefix2_hash[grp[0]] - val = self.model.get_value(handle, 0) - if val: - p = self.db.get_person_from_handle(grp[0]) - name = p.get_primary_name() - name.set_surname(grp[1].strip()) - name.set_surname_prefix(grp[2].strip()) - self.db.commit_person(p, trans) + for key, data in self.handle_to_action.items(): + p = self.db.get_person_from_handle(key) + if self.nickid in data: + modelhandle = self.nick_hash[key] + val = self.model.get_value(modelhandle, 0) + if val: + given, nick = data[self.nickid] + name = p.get_primary_name() + name.set_first_name(given.strip()) + name.set_nick_name(nick.strip()) + + if self.titleid in data: + modelhandle = self.title_hash[key] + val = self.model.get_value(modelhandle, 0) + if val: + title, given = data[self.titleid] + name = p.get_primary_name() + name.set_first_name(given.strip()) + name.set_title(title.strip()) + + if self.pref1id in data: + modelhandle = self.prefix1_hash[key] + val = self.model.get_value(modelhandle, 0) + if val: + given, prefixtotal, prefix = data[self.pref1id] + name = p.get_primary_name() + name.set_first_name(given.strip()) + oldpref = name.get_surname_list()[0].get_prefix().strip() + if oldpref == '' or oldpref == prefix.strip(): + name.get_surname_list()[0].set_prefix(prefix) + else: + name.get_surname_list()[0].set_prefix('%s %s' % (prefix, oldpref)) + + if self.compid in data: + modelhandle = self.compound_hash[key] + val = self.model.get_value(modelhandle, 0) + if val: + surns, prefs, cons, prims, origs = data[self.compid] + name = p.get_primary_name() + new_surn_list = [] + for surn, pref, con, prim, orig in zip(surns, prefs, cons, + prims, origs): + new_surn_list.append(gen.lib.Surname()) + new_surn_list[-1].set_surname(surn.strip()) + new_surn_list[-1].set_prefix(pref.strip()) + new_surn_list[-1].set_connector(con.strip()) + new_surn_list[-1].set_primary(prim) + new_surn_list[-1].set_origintype(orig) + name.set_surname_list(new_surn_list) + + self.db.commit_person(p, trans) self.db.transaction_commit(trans, _("Extract information from names")) @@ -357,3 +544,7 @@ class PatchNamesOptions(tool.ToolOptions): def __init__(self, name, person_id=None): tool.ToolOptions.__init__(self, name, person_id) + +def strip(arg): + return arg.strip() + From cf943afcd5e821782c36dc2c48e54b15c2402253 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sun, 24 Oct 2010 13:06:55 +0000 Subject: [PATCH 27/28] Finish patchname tool svn: r16033 --- src/plugins/tool/PatchNames.py | 26 ++++++++++++++++---------- src/plugins/tool/patchnames.glade | 16 +++++++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/plugins/tool/PatchNames.py b/src/plugins/tool/PatchNames.py index 706f8f87c..b55d67bef 100644 --- a/src/plugins/tool/PatchNames.py +++ b/src/plugins/tool/PatchNames.py @@ -132,7 +132,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): hboxcon.pack_start(self.conbox) winprefix.vbox.pack_start(hboxcon) hboxconns = gtk.HBox() - hboxconns.pack_start(gtk.Label(_('Connectors non-splitting surnames:')), + hboxconns.pack_start(gtk.Label(_('Connectors not splitting surnames:')), expand=False, padding=5) self.connsbox = gtk.Entry() self.connsbox.set_text(', '.join(CONNECTOR_LIST_NONSPLIT)) @@ -269,16 +269,23 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): cont = True for pref, surn, con, prim, orig in zip(old_prefix, old_surn, old_con, old_prim, old_orig): + surnval = surn.split() + if surnval == []: + new_prefix_list.append(pref) + new_surname_list.append('') + new_connector_list.append(con) + new_prim_list.append(prim) + new_orig_list.append(orig) + cont = False + continue + val = surnval.pop(0) while cont: new_prefix_list.append(pref) new_surname_list.append('') new_connector_list.append(con) new_prim_list.append(prim) new_orig_list.append(orig) - surnval = surn.split() - if surnval == []: - continue - val = surnval.pop(0) + while cont and (val.lower() in self.prefix_list): found = True if new_prefix_list[-1]: @@ -299,8 +306,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): val = '' cont = False #if value after surname indicates continue, then continue - if cont and ((val.lower() in self.connector_list_nonsplit) or - (new_surname_list[-1].lower() in self.connector_list_nonsplit)): + while cont and (val.lower() in self.connector_list_nonsplit): #add this val to the current surname new_surname_list[-1] += ' ' + val try: @@ -310,7 +316,8 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): cont = False # if previous is non-splitting connector, then add new val to # current surname - if cont and (new_surname_list[-1].lower() in self.connector_list_nonsplit): + if cont and (new_surname_list[-1].split()[-1].lower() \ + in self.connector_list_nonsplit): new_surname_list[-1] += ' ' + val try: val = surnval.pop(0) @@ -323,7 +330,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): if new_connector_list[-1]: new_connector_list[-1] = ' ' + val else: - new_prefix_list[-1] = val + new_connector_list[-1] = val try: val = surnval.pop(0) except IndexError: @@ -536,7 +543,6 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): self.close() self.cb() - class PatchNamesOptions(tool.ToolOptions): """ Defines options and provides handling interface. diff --git a/src/plugins/tool/patchnames.glade b/src/plugins/tool/patchnames.glade index cdce7df16..5ceb4c38e 100644 --- a/src/plugins/tool/patchnames.glade +++ b/src/plugins/tool/patchnames.glade @@ -12,12 +12,10 @@ True - vertical True 6 - vertical True @@ -32,10 +30,18 @@ True + 0 10 - Below is a list of the nicknames, titles and family name prefixes that Gramps can extract from the -current database. If you accept the changes, Gramps will modify the entries -that have been selected. + Below is a list of the nicknames, titles, prefixes and compound surnames that Gramps can extract from the family tree. +If you accept the changes, Gramps will modify the entries that have been selected. + +Compound surnames are shown as lists of [prefix, surname, connector]. +For example, with the defaults, the name "de Mascarenhas da Silva e Lencastre" shows as: + [de, Mascarenhas]-[da, Silva, e]-[,Lencastre] + +Run this tool several times to correct names that have multiple information that can be extracted. + True + 100 False From 405572cbdc96c0b5c991ab18229c37a2fc1684e6 Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sun, 24 Oct 2010 13:36:26 +0000 Subject: [PATCH 28/28] add to famtree with name preset svn: r16034 --- src/plugins/view/persontreeview.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/plugins/view/persontreeview.py b/src/plugins/view/persontreeview.py index 92b9fea3d..785e0d746 100644 --- a/src/plugins/view/persontreeview.py +++ b/src/plugins/view/persontreeview.py @@ -158,17 +158,27 @@ class PersonTreeView(BasePersonView): # attempt to get the current surname (model, pathlist) = self.selection.get_selected_rows() - name = u"" + name = gen.lib.Name() + basepers = None if len(pathlist) == 1: path = pathlist[0] if len(path) == 1: - name = model.on_get_iter(path).name - else: - node = model.on_get_iter(path) - name = model.on_iter_parent(node).name + path = (path[0], 0) + node = model.get_iter(path) + handle = model.get_value(node, self.handle_col) + basepers = self.dbstate.db.get_person_from_handle(handle) + if basepers: + surnlist = [] + primname = basepers.get_primary_name() + for surn in primname.get_surname_list(): + surnlist.append(gen.lib.Surname(source=surn)) + name.set_surname_list(surnlist) + name.set_family_nick_name(primname.get_family_nick_name()) + name.set_group_as(primname.get_group_as()) + name.set_sort_as(primname.get_sort_as()) try: - person.get_primary_name().set_surname(name) + person.set_primary_name(name) EditPerson(self.dbstate, self.uistate, [], person) except Errors.WindowActiveError: pass