1#
2# Gramps - a GTK+/GNOME based genealogy program
3#
4# Copyright (C) 2000-2007  Donald N. Allingham
5# Copyright (C) 2010       Michiel D. Nauta
6# Copyright (C) 2011       Tim G L Lyons
7# Copyright (C) 2013       Doug Blank <doug.blank@gmail.com>
8# Copyright (C) 2017       Nick Hall
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23#
24
25"""
26Name class for Gramps.
27"""
28
29#-------------------------------------------------------------------------
30#
31# Gramps modules
32#
33#-------------------------------------------------------------------------
34from .secondaryobj import SecondaryObject
35from .privacybase import PrivacyBase
36from .citationbase import CitationBase
37from .notebase import NoteBase
38from .datebase import DateBase
39from .surnamebase import SurnameBase
40from .nametype import NameType
41from .const import IDENTICAL, EQUAL, DIFFERENT
42from .date import Date
43from ..const import GRAMPS_LOCALE as glocale
44_ = glocale.translation.gettext
45
46#-------------------------------------------------------------------------
47#
48# Personal Name
49#
50#-------------------------------------------------------------------------
51class Name(SecondaryObject, PrivacyBase, SurnameBase, CitationBase, NoteBase,
52           DateBase):
53    """
54    Provide name information about a person.
55
56    A person may have more that one name throughout his or her life. The Name
57    object stores one of them
58    """
59
60    DEF = 0    # Default format (determined by gramps-wide prefs)
61    LNFN = 1   # last name first name
62    FNLN = 2   # first name last name
63    FN = 4     # first name
64    LNFNP = 5  # primary name primconnector rest, given pa/ma suffix, primprefix
65
66    NAMEFORMATS = (DEF, LNFN, FNLN, FN, LNFNP)
67    #deprecated :
68    PTFN = 3  # patronymic first name
69
70    def __init__(self, source=None, data=None):
71        """Create a new Name instance, copying from the source if provided.
72        We should connect here to 'person-groupname-rebuild' and do something
73        correct when first parameter is the name, and second parameter is
74        different from the group here. However, that would be complicated and
75        no real errors that cannot be ammended can be done if group is
76        saved differently.
77        """
78        PrivacyBase.__init__(self, source)
79        SurnameBase.__init__(self, source)
80        CitationBase.__init__(self, source)
81        NoteBase.__init__(self, source)
82        DateBase.__init__(self, source)
83        if data:
84            (privacy, citation_list, note, date,
85             self.first_name, surname_list, self.suffix, self.title, name_type,
86             self.group_as, self.sort_as, self.display_as, self.call,
87             self.nick, self.famnick) = data
88            self.type = NameType(name_type)
89            SurnameBase.unserialize(self, surname_list)
90            PrivacyBase.unserialize(self, privacy)
91            CitationBase.unserialize(self, citation_list)
92            NoteBase.unserialize(self, note)
93            DateBase.unserialize(self, date)
94        elif source:
95            self.first_name = source.first_name
96            self.suffix = source.suffix
97            self.title = source.title
98            self.type = NameType(source.type)
99            self.group_as = source.group_as
100            self.sort_as = source.sort_as
101            self.display_as = source.display_as
102            self.call = source.call
103            self.nick = source.nick
104            self.famnick = source.famnick
105        else:
106            self.first_name = ""
107            self.suffix = ""
108            self.title = ""
109            self.type = NameType()
110            self.group_as = ""
111            self.sort_as = self.DEF
112            self.display_as = self.DEF
113            self.call = ""
114            self.nick = ""
115            self.famnick = ""
116
117    def serialize(self):
118        """
119        Convert the object to a serialized tuple of data.
120        """
121        return (PrivacyBase.serialize(self),
122                CitationBase.serialize(self),
123                NoteBase.serialize(self),
124                DateBase.serialize(self),
125                self.first_name,
126                SurnameBase.serialize(self),
127                self.suffix, self.title,
128                self.type.serialize(),
129                self.group_as, self.sort_as, self.display_as, self.call,
130                self.nick, self.famnick)
131
132    @classmethod
133    def get_schema(cls):
134        """
135        Returns the JSON Schema for this class.
136
137        :returns: Returns a dict containing the schema.
138        :rtype: dict
139        """
140        from .surname import Surname
141        return {
142            "type": "object",
143            "title": _("Name"),
144            "properties": {
145                "_class": {"enum": [cls.__name__]},
146                "private": {"type": "boolean",
147                            "title": _("Private")},
148                "citation_list": {"type": "array",
149                                  "items": {"type": "string",
150                                            "maxLength": 50},
151                                  "title": _("Citations")},
152                "note_list": {"type": "array",
153                              "items": {"type": "string",
154                                        "maxLength": 50},
155                              "title": _("Notes")},
156                "date": {"oneOf": [{"type": "null"}, Date.get_schema()],
157                         "title": _("Date")},
158                "first_name": {"type": "string",
159                               "title": _("Given name")},
160                "surname_list": {"type": "array",
161                                 "items": Surname.get_schema(),
162                                 "title": _("Surnames")},
163                "suffix": {"type": "string",
164                           "title": _("Suffix")},
165                "title": {"type": "string",
166                          "title": _("Title")},
167                "type": NameType.get_schema(),
168                "group_as": {"type": "string",
169                             "title": _("Group as")},
170                "sort_as": {"type": "integer",
171                            "title": _("Sort as")},
172                "display_as": {"type": "integer",
173                               "title": _("Display as")},
174                "call": {"type": "string",
175                         "title": _("Call name")},
176                "nick": {"type": "string",
177                         "title": _("Nick name")},
178                "famnick": {"type": "string",
179                            "title": _("Family nick name")}
180            }
181        }
182
183    def is_empty(self):
184        """
185        Indicate if the name is empty.
186        """
187        namefieldsempty = (self.first_name == "" and
188                           self.suffix == "" and
189                           self.title == "" and
190                           self.nick == "" and
191                           self.famnick == "")
192        surnamefieldsempty = False not in [surn.is_empty()
193                                           for surn in self.surname_list]
194        return namefieldsempty and surnamefieldsempty
195
196    def unserialize(self, data):
197        """
198        Convert a serialized tuple of data to an object.
199        """
200        (privacy, citation_list, note_list, date,
201         self.first_name, surname_list, self.suffix, self.title, name_type,
202         self.group_as, self.sort_as, self.display_as, self.call,
203         self.nick, self.famnick) = data
204        self.type = NameType(name_type)
205        PrivacyBase.unserialize(self, privacy)
206        SurnameBase.unserialize(self, surname_list)
207        CitationBase.unserialize(self, citation_list)
208        NoteBase.unserialize(self, note_list)
209        DateBase.unserialize(self, date)
210        return self
211
212    def get_text_data_list(self):
213        """
214        Return the list of all textual attributes of the object.
215
216        :returns: Returns the list of all textual attributes of the object.
217        :rtype: list
218        """
219        return [self.first_name, self.suffix, self.title,
220                str(self.type), self.call, self.nick, self.famnick]
221
222    def get_text_data_child_list(self):
223        """
224        Return the list of child objects that may carry textual data.
225
226        :returns: Returns the list of child objects that may carry textual data.
227        :rtype: list
228        """
229        return self.surname_list
230
231    def get_note_child_list(self):
232        """
233        Return the list of child secondary objects that may refer notes.
234
235        :returns: Returns the list of child secondary child objects that may
236                  refer notes.
237        :rtype: list
238        """
239        return []
240
241    def get_handle_referents(self):
242        """
243        Return the list of child objects which may, directly or through
244        their children, reference primary objects.
245
246        :returns: Returns the list of objects referencing primary objects.
247        :rtype: list
248        """
249        return []
250
251    def get_referenced_handles(self):
252        """
253        Return the list of (classname, handle) tuples for all directly
254        referenced primary objects.
255
256        :returns: List of (classname, handle) tuples for referenced objects.
257        :rtype: list
258        """
259        return self.get_referenced_note_handles() + \
260               self.get_referenced_citation_handles()
261
262    def is_equivalent(self, other):
263        """
264        Return if this name is equivalent, that is agrees in type, first,
265        call, surname_list, suffix, title and date, to other.
266
267        :param other: The name to compare this name to.
268        :type other: Name
269        :returns: Constant indicating degree of equivalence.
270        :rtype: int
271        """
272        # TODO what to do with sort and display?
273        if self.get_text_data_list() != other.get_text_data_list() or \
274            self.get_date_object() != other.get_date_object() or \
275            SurnameBase.serialize(self) != SurnameBase.serialize(other):
276            return DIFFERENT
277        else:
278            if self.is_equal(other):
279                return IDENTICAL
280            else:
281                return EQUAL
282
283    def merge(self, acquisition):
284        """
285        Merge the content of acquisition into this name.
286        Normally the person merge code should opt for adding an alternate
287        name if names are actually different (like not equal surname list)
288
289        Lost: type, first, call, suffix, title, nick, famnick and date of
290        acquisition.
291
292        :param acquisition: The name to merge with the present name.
293        :type acquisition: Name
294        """
295        # TODO what to do with sort and display?
296        self._merge_privacy(acquisition)
297        self._merge_surname_list(acquisition)
298        self._merge_note_list(acquisition)
299        self._merge_citation_list(acquisition)
300
301    def set_group_as(self, name):
302        """
303        Set the grouping name for a person.
304
305        Normally, this is the person's surname. However, some locales group
306        equivalent names (e.g. Ivanova and Ivanov in Russian are usually
307        considered equivalent.
308
309        .. note:: There is also a database wide grouping set_name_group_mapping
310          So one might map a name Smith to SmithNew, and have one person still
311          grouped with name Smith. Hence, group_as can be equal to surname!
312        """
313        self.group_as = name
314
315    def get_group_as(self):
316        """
317        Return the grouping name, which is used to group equivalent surnames.
318        """
319        return self.group_as
320
321    def get_group_name(self):
322        """
323        Return the grouping name, which is used to group equivalent surnames.
324        """
325        if self.group_as:
326            return self.group_as
327        else:
328            return self.get_primary_surname().get_surname()
329
330    def set_sort_as(self, value):
331        """
332        Specifies the sorting method for the specified name.
333
334        Typically the locale's default should be used. However, there may be
335        names where a specific sorting structure is desired for a name.
336        """
337        self.sort_as = value
338
339    def get_sort_as(self):
340        """
341        Return the selected sorting method for the name.
342
343        The options are LNFN (last name, first name), FNLN (first name, last
344        name), etc.
345        """
346        return self.sort_as
347
348    def set_display_as(self, value):
349        """
350        Specifies the display format for the specified name.
351
352        Typically the locale's default should be used. However, there may be
353        names where a specific display format is desired for a name.
354        """
355        self.display_as = value
356
357    def get_display_as(self):
358        """
359        Return the selected display format for the name.
360
361        The options are LNFN (last name, first name), FNLN (first name, last
362        name), etc.
363        """
364        return self.display_as
365
366    def get_call_name(self):
367        """
368        Return the call name.
369
370        The call name's exact definition is not predetermined, and may be
371        locale specific.
372        """
373        return self.call
374
375    def set_call_name(self, val):
376        """
377        Set the call name.
378
379        The call name's exact definition is not predetermined, and may be
380        locale specific.
381        """
382        self.call = val
383
384    def get_nick_name(self):
385        """
386        Return the nick name.
387
388        The nick name of the person, a not official name the person is known
389        with.
390        """
391        return self.nick
392
393    def set_nick_name(self, val):
394        """
395        Set the nick name.
396
397        The nick name of the person, a not official name the person is known
398        with.
399        """
400        self.nick = val
401
402    def get_family_nick_name(self):
403        """
404        Return the family nick name.
405
406        The family nick name of the family of the person, a not official name
407        use to denote the entire family.
408        """
409        return self.famnick
410
411    def set_family_nick_name(self, val):
412        """
413        Set the family nick name.
414
415        The family nick name of the family of the person, a not official name
416        use to denote the entire family.
417        """
418        self.famnick = val
419
420    def set_type(self, the_type):
421        """Set the type of the Name instance."""
422        self.type.set(the_type)
423
424    def get_type(self):
425        """Return the type of the Name instance."""
426        return self.type
427
428    def set_first_name(self, name):
429        """Set the given name for the Name instance."""
430        self.first_name = name
431
432    def get_first_name(self):
433        """Return the given name for the Name instance."""
434        return self.first_name
435
436    def set_suffix(self, name):
437        """Set the suffix (such as Jr., III, etc.) for the Name instance."""
438        self.suffix = name
439
440    def get_suffix(self):
441        """Return the suffix for the Name instance."""
442        return self.suffix
443
444    def set_title(self, title):
445        """Set the title (Dr., Reverand, Captain) for the Name instance."""
446        self.title = title
447
448    def get_title(self):
449        """Return the title for the Name instance."""
450        return self.title
451
452    def get_name(self):
453        """
454        Return a name string built from the components of the Name instance,
455        in the form of: surname, Firstname.
456        """
457        first = self.first_name
458        surname = self.get_surname()
459        if self.suffix:
460            # translators: needed for Arabic, ignore otherwise
461            return _("%(surname)s, %(first)s %(suffix)s"
462                    ) % {'surname':surname, 'first':first, 'suffix':self.suffix}
463        else:
464            # translators: needed for Arabic, ignore otherwise
465            return _("%(str1)s, %(str2)s") % {'str1':surname, 'str2':first}
466
467    def get_upper_name(self):
468        """
469        Return a name string built from the components of the Name instance,
470        in the form of SURNAME, Firstname.
471        """
472        first = self.first_name
473        surname = self.get_surname().upper()
474        if self.suffix:
475            # translators: needed for Arabic, ignore otherwise
476            return _("%(surname)s, %(first)s %(suffix)s"
477                    ) % {'surname':surname, 'first':first, 'suffix':self.suffix}
478        else:
479            # translators: needed for Arabic, ignore otherwise
480            return _("%(str1)s, %(str2)s") % {'str1':surname, 'str2':first}
481
482    def get_regular_name(self):
483        """
484        Return a name string built from the components of the Name instance,
485        in the form of Firstname surname.
486        """
487        first = self.first_name
488        surname = self.get_surname()
489        if self.suffix == "":
490            return "%s %s" % (first, surname)
491        else:
492            # translators: needed for Arabic, ignore otherwise
493            return _("%(first)s %(surname)s, %(suffix)s"
494                    ) % {'surname':surname, 'first':first, 'suffix':self.suffix}
495
496    def get_gedcom_parts(self):
497        """
498        Returns a GEDCOM-formatted name dictionary.
499
500        .. note:: Fields patronymic and prefix are deprecated, prefix_list and
501                  surname list, added.
502        """
503        retval = {}
504        retval['given'] = self.first_name.strip()
505        retval['surname'] = self.get_surname().replace('/', '?')
506        retval['suffix'] = self.suffix
507        retval['title'] = self.title
508        retval['surnamelist'] = self.get_surnames()
509        retval['prefixes'] = self.get_prefixes()
510        retval['connectors'] = self.get_connectors()
511        retval['nick'] = self.nick
512        retval['famnick'] = self.famnick
513        return retval
514
515    def get_gedcom_name(self):
516        """
517        Returns a GEDCOM-formatted name.
518        """
519        firstname = self.first_name.strip()
520        surname = self.get_surname().replace('/', '?')
521        suffix = self.suffix
522        if suffix == "":
523            return '%s /%s/' % (firstname, surname)
524        else:
525            return '%s /%s/ %s' % (firstname, surname, suffix)
526