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) 2010,2017 Nick Hall 7# Copyright (C) 2011 Tim G L Lyons 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program; if not, write to the Free Software 21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22# 23 24""" 25Person object for Gramps. 26""" 27 28#------------------------------------------------------------------------- 29# 30# Gramps modules 31# 32#------------------------------------------------------------------------- 33from .primaryobj import PrimaryObject 34from .citationbase import CitationBase 35from .notebase import NoteBase 36from .mediabase import MediaBase 37from .attrbase import AttributeBase 38from .addressbase import AddressBase 39from .ldsordbase import LdsOrdBase 40from .urlbase import UrlBase 41from .tagbase import TagBase 42from .name import Name 43from .eventref import EventRef 44from .personref import PersonRef 45from .attrtype import AttributeType 46from .eventroletype import EventRoleType 47from .attribute import Attribute 48from .const import IDENTICAL, EQUAL, DIFFERENT 49from ..const import GRAMPS_LOCALE as glocale 50_ = glocale.translation.gettext 51 52#------------------------------------------------------------------------- 53# 54# Person class 55# 56#------------------------------------------------------------------------- 57class Person(CitationBase, NoteBase, AttributeBase, MediaBase, 58 AddressBase, UrlBase, LdsOrdBase, PrimaryObject): 59 """ 60 The Person record is the Gramps in-memory representation of an 61 individual person. It contains all the information related to 62 an individual. 63 64 Person objects are usually created in one of two ways. 65 66 1. Creating a new person object, which is then initialized and added to 67 the database. 68 2. Retrieving an object from the database using the records handle. 69 70 Once a Person object has been modified, it must be committed 71 to the database using the database object's commit_person function, 72 or the changes will be lost. 73 74 """ 75 76 UNKNOWN = 2 77 MALE = 1 78 FEMALE = 0 79 80 def __init__(self, data=None): 81 """ 82 Create a new Person instance. 83 84 After initialization, most data items have empty or null values, 85 including the database handle. 86 """ 87 PrimaryObject.__init__(self) 88 CitationBase.__init__(self) 89 NoteBase.__init__(self) 90 MediaBase.__init__(self) 91 AttributeBase.__init__(self) 92 AddressBase.__init__(self) 93 UrlBase.__init__(self) 94 LdsOrdBase.__init__(self) 95 self.primary_name = Name() 96 self.event_ref_list = [] 97 self.family_list = [] 98 self.parent_family_list = [] 99 self.alternate_names = [] 100 self.person_ref_list = [] 101 self.__gender = Person.UNKNOWN 102 self.death_ref_index = -1 103 self.birth_ref_index = -1 104 if data: 105 self.unserialize(data) 106 107 # We hold a reference to the GrampsDB so that we can maintain 108 # its genderStats. It doesn't get set here, but from 109 # GenderStats.count_person. 110 111 def __eq__(self, other): 112 return isinstance(other, Person) and self.handle == other.handle 113 114 def __ne__(self, other): 115 return not self == other 116 117 def serialize(self): 118 """ 119 Convert the data held in the Person to a Python tuple that 120 represents all the data elements. 121 122 This method is used to convert the object into a form that can easily 123 be saved to a database. 124 125 These elements may be primitive Python types (string, integers), 126 complex Python types (lists or tuples, or Python objects. If the 127 target database cannot handle complex types (such as objects or 128 lists), the database is responsible for converting the data into 129 a form that it can use. 130 131 :returns: Returns a python tuple containing the data that should 132 be considered persistent. 133 :rtype: tuple 134 """ 135 return ( 136 self.handle, # 0 137 self.gramps_id, # 1 138 self.__gender, # 2 139 self.primary_name.serialize(), # 3 140 [name.serialize() for name in self.alternate_names], # 4 141 self.death_ref_index, # 5 142 self.birth_ref_index, # 6 143 [er.serialize() for er in self.event_ref_list], # 7 144 self.family_list, # 8 145 self.parent_family_list, # 9 146 MediaBase.serialize(self), # 10 147 AddressBase.serialize(self), # 11 148 AttributeBase.serialize(self), # 12 149 UrlBase.serialize(self), # 13 150 LdsOrdBase.serialize(self), # 14 151 CitationBase.serialize(self), # 15 152 NoteBase.serialize(self), # 16 153 self.change, # 17 154 TagBase.serialize(self), # 18 155 self.private, # 19 156 [pr.serialize() for pr in self.person_ref_list] # 20 157 ) 158 159 @classmethod 160 def get_schema(cls): 161 """ 162 Returns the JSON Schema for this class. 163 164 :returns: Returns a dict containing the schema. 165 :rtype: dict 166 """ 167 from .mediaref import MediaRef 168 from .address import Address 169 from .url import Url 170 from .ldsord import LdsOrd 171 return { 172 "type": "object", 173 "title": _("Person"), 174 "properties": { 175 "_class": {"enum": [cls.__name__]}, 176 "handle": {"type": "string", 177 "maxLength": 50, 178 "title": _("Handle")}, 179 "gramps_id": {"type": "string", 180 "title": _("Gramps ID")}, 181 "gender": {"type": "integer", 182 "minimum": 0, 183 "maximum": 2, 184 "title": _("Gender")}, 185 "primary_name": Name.get_schema(), 186 "alternate_names": {"type": "array", 187 "items": Name.get_schema(), 188 "title": _("Alternate names")}, 189 "death_ref_index": {"type": "integer", 190 "title": _("Death reference index")}, 191 "birth_ref_index": {"type": "integer", 192 "title": _("Birth reference index")}, 193 "event_ref_list": {"type": "array", 194 "items": EventRef.get_schema(), 195 "title": _("Event references")}, 196 "family_list": {"type": "array", 197 "items": {"type": "string", 198 "maxLength": 50}, 199 "title": _("Families")}, 200 "parent_family_list": {"type": "array", 201 "items": {"type": "string", 202 "maxLength": 50}, 203 "title": _("Parent families")}, 204 "media_list": {"type": "array", 205 "items": MediaRef.get_schema(), 206 "title": _("Media")}, 207 "address_list": {"type": "array", 208 "items": Address.get_schema(), 209 "title": _("Addresses")}, 210 "attribute_list": {"type": "array", 211 "items": Attribute.get_schema(), 212 "title": _("Attributes")}, 213 "urls": {"type": "array", 214 "items": Url.get_schema(), 215 "title": _("Urls")}, 216 "lds_ord_list": {"type": "array", 217 "items": LdsOrd.get_schema(), 218 "title": _("LDS ordinances")}, 219 "citation_list": {"type": "array", 220 "items": {"type": "string", 221 "maxLength": 50}, 222 "title": _("Citations")}, 223 "note_list": {"type": "array", 224 "items": {"type": "string", 225 "maxLength": 50}, 226 "title": _("Notes")}, 227 "change": {"type": "integer", 228 "title": _("Last changed")}, 229 "tag_list": {"type": "array", 230 "items": {"type": "string", 231 "maxLength": 50}, 232 "title": _("Tags")}, 233 "private": {"type": "boolean", 234 "title": _("Private")}, 235 "person_ref_list": {"type": "array", 236 "items": PersonRef.get_schema(), 237 "title": _("Person references")} 238 } 239 } 240 241 def unserialize(self, data): 242 """ 243 Convert the data held in a tuple created by the serialize method 244 back into the data in a Person object. 245 246 :param data: tuple containing the persistent data associated the 247 Person object 248 :type data: tuple 249 """ 250 (self.handle, # 0 251 self.gramps_id, # 1 252 self.__gender, # 2 253 primary_name, # 3 254 alternate_names, # 4 255 self.death_ref_index, # 5 256 self.birth_ref_index, # 6 257 event_ref_list, # 7 258 self.family_list, # 8 259 self.parent_family_list, # 9 260 media_list, # 10 261 address_list, # 11 262 attribute_list, # 12 263 urls, # 13 264 lds_ord_list, # 14 265 citation_list, # 15 266 note_list, # 16 267 self.change, # 17 268 tag_list, # 18 269 self.private, # 19 270 person_ref_list, # 20 271 ) = data 272 273 self.primary_name = Name() 274 self.primary_name.unserialize(primary_name) 275 self.alternate_names = [Name().unserialize(name) 276 for name in alternate_names] 277 self.event_ref_list = [EventRef().unserialize(er) 278 for er in event_ref_list] 279 self.person_ref_list = [PersonRef().unserialize(pr) 280 for pr in person_ref_list] 281 MediaBase.unserialize(self, media_list) 282 LdsOrdBase.unserialize(self, lds_ord_list) 283 AddressBase.unserialize(self, address_list) 284 AttributeBase.unserialize(self, attribute_list) 285 UrlBase.unserialize(self, urls) 286 CitationBase.unserialize(self, citation_list) 287 NoteBase.unserialize(self, note_list) 288 TagBase.unserialize(self, tag_list) 289 return self 290 291 def _has_handle_reference(self, classname, handle): 292 """ 293 Return True if the object has reference to a given handle of given 294 primary object type. 295 296 :param classname: The name of the primary object class. 297 :type classname: str 298 :param handle: The handle to be checked. 299 :type handle: str 300 :returns: Returns whether the object has reference to this handle of 301 this object type. 302 :rtype: bool 303 """ 304 if classname == 'Event': 305 return any(ref.ref == handle for ref in self.event_ref_list) 306 elif classname == 'Person': 307 return any(ref.ref == handle for ref in self.person_ref_list) 308 elif classname == 'Family': 309 return any(ref == handle 310 for ref in self.family_list + self.parent_family_list + 311 [ordinance.famc for ordinance in self.lds_ord_list]) 312 elif classname == 'Place': 313 return any(ordinance.place == handle 314 for ordinance in self.lds_ord_list) 315 return False 316 317 def _remove_handle_references(self, classname, handle_list): 318 if classname == 'Event': 319 # Keep a copy of the birth and death references 320 birth_ref = self.get_birth_ref() 321 death_ref = self.get_death_ref() 322 323 new_list = [ref for ref in self.event_ref_list 324 if ref.ref not in handle_list] 325 # If deleting removing the reference to the event 326 # to which birth or death ref_index points, unset the index 327 if (self.birth_ref_index != -1 328 and self.event_ref_list[self.birth_ref_index].ref 329 in handle_list): 330 self.set_birth_ref(None) 331 if (self.death_ref_index != -1 332 and self.event_ref_list[self.death_ref_index].ref 333 in handle_list): 334 self.set_death_ref(None) 335 self.event_ref_list = new_list 336 337 # Reset the indexes after deleting the event from even_ref_list 338 if self.birth_ref_index != -1: 339 self.set_birth_ref(birth_ref) 340 if self.death_ref_index != -1: 341 self.set_death_ref(death_ref) 342 elif classname == 'Person': 343 new_list = [ref for ref in self.person_ref_list 344 if ref.ref not in handle_list] 345 self.person_ref_list = new_list 346 elif classname == 'Family': 347 new_list = [handle for handle in self.family_list 348 if handle not in handle_list] 349 self.family_list = new_list 350 new_list = [handle for handle in self.parent_family_list 351 if handle not in handle_list] 352 self.parent_family_list = new_list 353 for ordinance in self.lds_ord_list: 354 if ordinance.famc in handle_list: 355 ordinance.famc = None 356 elif classname == 'Place': 357 for ordinance in self.lds_ord_list: 358 if ordinance.place in handle_list: 359 ordinance.place = None 360 361 def _replace_handle_reference(self, classname, old_handle, new_handle): 362 if classname == 'Event': 363 refs_list = [ref.ref for ref in self.event_ref_list] 364 new_ref = None 365 if new_handle in refs_list: 366 new_ref = self.event_ref_list[refs_list.index(new_handle)] 367 n_replace = refs_list.count(old_handle) 368 for ix_replace in range(n_replace): 369 idx = refs_list.index(old_handle) 370 self.event_ref_list[idx].ref = new_handle 371 refs_list[idx] = new_handle 372 if new_ref: 373 evt_ref = self.event_ref_list[idx] 374 equi = new_ref.is_equivalent(evt_ref) 375 if equi != DIFFERENT: 376 if equi == EQUAL: 377 new_ref.merge(evt_ref) 378 self.event_ref_list.pop(idx) 379 refs_list.pop(idx) 380 if idx < self.birth_ref_index: 381 self.birth_ref_index -= 1 382 elif idx == self.birth_ref_index: 383 self.birth_ref_index = -1 384 # birth_ref_index should be recalculated which 385 # needs database access! 386 if idx < self.death_ref_index: 387 self.death_ref_index -= 1 388 elif idx == self.death_ref_index: 389 self.death_ref_index = -1 390 # death_ref_index should be recalculated which 391 # needs database access! 392 elif classname == 'Person': 393 refs_list = [ref.ref for ref in self.person_ref_list] 394 new_ref = None 395 if new_handle in refs_list: 396 new_ref = self.person_ref_list[refs_list.index(new_handle)] 397 n_replace = refs_list.count(old_handle) 398 for ix_replace in range(n_replace): 399 idx = refs_list.index(old_handle) 400 self.person_ref_list[idx].ref = new_handle 401 refs_list[idx] = new_handle 402 if new_ref: 403 person_ref = self.person_ref_list[idx] 404 equi = new_ref.is_equivalent(person_ref) 405 if equi != DIFFERENT: 406 if equi == EQUAL: 407 new_ref.merge(person_ref) 408 self.person_ref_list.pop(idx) 409 refs_list.pop(idx) 410 elif classname == 'Family': 411 while old_handle in self.family_list: 412 ix = self.family_list.index(old_handle) 413 self.family_list[ix] = new_handle 414 while old_handle in self.parent_family_list: 415 ix = self.parent_family_list.index(old_handle) 416 self.parent_family_list[ix] = new_handle 417 handle_list = [ordinance.famc for ordinance in self.lds_ord_list] 418 while old_handle in handle_list: 419 ix = handle_list.index(old_handle) 420 self.lds_ord_list[ix].famc = new_handle 421 handle_list[ix] = '' 422 elif classname == "Place": 423 handle_list = [ordinance.place for ordinance in self.lds_ord_list] 424 while old_handle in handle_list: 425 ix = handle_list.index(old_handle) 426 self.lds_ord_list[ix].place = new_handle 427 handle_list[ix] = '' 428 429 def get_text_data_list(self): 430 """ 431 Return the list of all textual attributes of the object. 432 433 :returns: Returns the list of all textual attributes of the object. 434 :rtype: list 435 """ 436 return [self.gramps_id] 437 438 def get_text_data_child_list(self): 439 """ 440 Return the list of child objects that may carry textual data. 441 442 :returns: Returns the list of child objects that may carry textual data. 443 :rtype: list 444 """ 445 check_list = self.lds_ord_list 446 add_list = [_f for _f in check_list if _f] 447 return ([self.primary_name] + 448 self.media_list + 449 self.alternate_names + 450 self.address_list + 451 self.attribute_list + 452 self.urls + 453 self.event_ref_list + 454 add_list + 455 self.person_ref_list 456 ) 457 458 def get_citation_child_list(self): 459 """ 460 Return the list of child secondary objects that may refer citations. 461 462 :returns: Returns the list of child secondary child objects that may 463 refer citations. 464 :rtype: list 465 """ 466 return ([self.primary_name] + 467 self.media_list + 468 self.alternate_names + 469 self.address_list + 470 self.attribute_list + 471 self.lds_ord_list + 472 self.person_ref_list + 473 self.event_ref_list 474 ) 475 476 def get_note_child_list(self): 477 """ 478 Return the list of child secondary objects that may refer notes. 479 480 :returns: Returns the list of child secondary child objects that may 481 refer notes. 482 :rtype: list 483 """ 484 return ([self.primary_name] + 485 self.media_list + 486 self.alternate_names + 487 self.address_list + 488 self.attribute_list + 489 self.lds_ord_list + 490 self.person_ref_list + 491 self.event_ref_list 492 ) 493 494 def get_referenced_handles(self): 495 """ 496 Return the list of (classname, handle) tuples for all directly 497 referenced primary objects. 498 499 :returns: List of (classname, handle) tuples for referenced objects. 500 :rtype: list 501 """ 502 return [('Family', handle) for handle in 503 (self.family_list + self.parent_family_list)] + ( 504 self.get_referenced_note_handles() + 505 self.get_referenced_citation_handles() + 506 self.get_referenced_tag_handles() 507 ) 508 509 def get_handle_referents(self): 510 """ 511 Return the list of child objects which may, directly or through 512 their children, reference primary objects. 513 514 :returns: Returns the list of objects referencing primary objects. 515 :rtype: list 516 """ 517 return ([self.primary_name] + 518 self.media_list + 519 self.alternate_names + 520 self.address_list + 521 self.attribute_list + 522 self.lds_ord_list + 523 self.person_ref_list + 524 self.event_ref_list 525 ) 526 527 def merge(self, acquisition): 528 """ 529 Merge the content of acquisition into this person. 530 531 :param acquisition: The person to merge with the present person. 532 :type acquisition: Person 533 """ 534 acquisition_id = acquisition.get_gramps_id() 535 if acquisition_id: 536 attr = Attribute() 537 attr.set_type(_("Merged Gramps ID")) 538 attr.set_value(acquisition.get_gramps_id()) 539 self.add_attribute(attr) 540 541 self._merge_privacy(acquisition) 542 acquisition.alternate_names.insert(0, acquisition.get_primary_name()) 543 self._merge_alternate_names(acquisition) 544 self._merge_event_ref_list(acquisition) 545 self._merge_lds_ord_list(acquisition) 546 self._merge_media_list(acquisition) 547 self._merge_address_list(acquisition) 548 self._merge_attribute_list(acquisition) 549 self._merge_url_list(acquisition) 550 self._merge_person_ref_list(acquisition) 551 self._merge_note_list(acquisition) 552 self._merge_citation_list(acquisition) 553 self._merge_tag_list(acquisition) 554 555 list(map(self.add_parent_family_handle, 556 acquisition.get_parent_family_handle_list())) 557 list(map(self.add_family_handle, acquisition.get_family_handle_list())) 558 559 def set_primary_name(self, name): 560 """ 561 Set the primary name of the Person to the specified :class:`~.name.Name` 562 instance. 563 564 :param name: :class:`~.name.Name` to be assigned to the person 565 :type name: :class:`~.name.Name` 566 """ 567 self.primary_name = name 568 569 def get_primary_name(self): 570 """ 571 Return the :class:`~.name.Name` instance marked as the Person's primary 572 name. 573 574 :returns: Returns the primary name 575 :rtype: :class:`~.name.Name` 576 """ 577 return self.primary_name 578 579 def get_alternate_names(self): 580 """ 581 Return the list of alternate :class:`~.name.Name` instances. 582 583 :returns: List of :class:`~.name.Name` instances 584 :rtype: list 585 """ 586 return self.alternate_names 587 588 def set_alternate_names(self, alt_name_list): 589 """ 590 Change the list of alternate names to the passed list. 591 592 :param alt_name_list: List of :class:`~.name.Name` instances 593 :type alt_name_list: list 594 """ 595 self.alternate_names = alt_name_list 596 597 def _merge_alternate_names(self, acquisition): 598 """ 599 Merge the list of alternate names from acquisition with our own. 600 601 :param acquisition: the list of alternate names of this object will be 602 merged with the current alternate name list. 603 :rtype acquisition: Person 604 """ 605 name_list = self.alternate_names[:] 606 primary_name = self.get_primary_name() 607 if primary_name and not primary_name.is_empty(): 608 name_list.insert(0, primary_name) 609 for addendum in acquisition.get_alternate_names(): 610 for name in name_list: 611 equi = name.is_equivalent(addendum) 612 if equi == IDENTICAL: 613 break 614 elif equi == EQUAL: 615 name.merge(addendum) 616 break 617 else: 618 self.alternate_names.append(addendum) 619 620 def add_alternate_name(self, name): 621 """ 622 Add a :class:`~.name.Name` instance to the list of alternative names. 623 624 :param name: :class:`~.name.Name` to add to the list 625 :type name: :class:`~.name.Name` 626 """ 627 self.alternate_names.append(name) 628 629 def get_nick_name(self): 630 for name in [self.get_primary_name()] + self.get_alternate_names(): 631 if name.get_nick_name(): 632 return name.get_nick_name() 633 for attr in self.attribute_list: 634 if int(attr.type) == AttributeType.NICKNAME: 635 return attr.get_value() 636 return '' 637 638 def set_gender(self, gender): 639 """ 640 Set the gender of the Person. 641 642 :param gender: Assigns the Person's gender to one of the 643 following constants: 644 645 - Person.MALE 646 - Person.FEMALE 647 - Person.UNKNOWN 648 :type gender: int 649 """ 650 if gender not in (Person.MALE, Person.FEMALE, Person.UNKNOWN): 651 raise ValueError('Attempt to assign invalid gender') 652 self.__gender = gender 653 654 def get_gender(self): 655 """ 656 Return the gender of the Person. 657 658 :returns: Returns one of the following constants: 659 660 - Person.MALE 661 - Person.FEMALE 662 - Person.UNKNOWN 663 :rtype: int 664 """ 665 return self.__gender 666 667 gender = property(get_gender, set_gender, None, 668 'Returns or sets the gender of the person') 669 670 def set_birth_ref(self, event_ref): 671 """ 672 Assign the birth event to the Person object. 673 674 This is accomplished by assigning the :class:`~.eventref.EventRef` of 675 the birth event in the current database. 676 677 :param event_ref: the :class:`~.eventref.EventRef` object associated 678 with the Person's birth. 679 :type event_ref: EventRef 680 """ 681 if event_ref and not isinstance(event_ref, EventRef): 682 raise ValueError("Expecting EventRef instance") 683 if event_ref is None: 684 self.birth_ref_index = -1 685 return 686 687 # check whether we already have this ref in the list 688 for self.birth_ref_index, ref in enumerate(self.event_ref_list): 689 if event_ref.is_equal(ref): 690 return # Note: self.birth_ref_index already set 691 692 self.event_ref_list.append(event_ref) 693 self.birth_ref_index = len(self.event_ref_list)-1 694 695 def set_death_ref(self, event_ref): 696 """ 697 Assign the death event to the Person object. 698 699 This is accomplished by assigning the :class:`~.eventref.EventRef` of 700 the death event in the current database. 701 702 :param event_ref: the :class:`~.eventref.EventRef` object associated 703 with the Person's death. 704 :type event_ref: EventRef 705 """ 706 if event_ref and not isinstance(event_ref, EventRef): 707 raise ValueError("Expecting EventRef instance") 708 if event_ref is None: 709 self.death_ref_index = -1 710 return 711 712 # check whether we already have this ref in the list 713 for self.death_ref_index, ref in enumerate(self.event_ref_list): 714 if event_ref.is_equal(ref): 715 return # Note: self.death_ref_index already set 716 717 self.event_ref_list.append(event_ref) 718 self.death_ref_index = len(self.event_ref_list)-1 719 720 def get_birth_ref(self): 721 """ 722 Return the :class:`~.eventref.EventRef` for Person's birth event. 723 724 This should correspond to an :class:`~.event.Event` in the database's 725 :class:`~.event.Event` list. 726 727 :returns: Returns the birth :class:`~.eventref.EventRef` or None if no 728 birth :class:`~.event.Event` has been assigned. 729 :rtype: EventRef 730 """ 731 732 if 0 <= self.birth_ref_index < len(self.event_ref_list): 733 return self.event_ref_list[self.birth_ref_index] 734 else: 735 return None 736 737 def get_death_ref(self): 738 """ 739 Return the :class:`~.eventref.EventRef` for the Person's death event. 740 741 This should correspond to an :class:`~.event.Event` in the database's 742 :class:`~.event.Event` list. 743 744 :returns: Returns the death :class:`~.eventref.EventRef` or None if no 745 death :class:`~.event.Event` has been assigned. 746 :rtype: event_ref 747 """ 748 749 if 0 <= self.death_ref_index < len(self.event_ref_list): 750 return self.event_ref_list[self.death_ref_index] 751 else: 752 return None 753 754 def add_event_ref(self, event_ref): 755 """ 756 Add the :class:`~.eventref.EventRef` to the Person instance's 757 :class:`~.eventref.EventRef` list. 758 759 This is accomplished by assigning the :class:`~.eventref.EventRef` of a 760 valid :class:`~.event.Event` in the current database. 761 762 :param event_ref: the :class:`~.eventref.EventRef` to be added to the 763 Person's :class:`~.eventref.EventRef` list. 764 :type event_ref: EventRef 765 """ 766 if event_ref and not isinstance(event_ref, EventRef): 767 raise ValueError("Expecting EventRef instance") 768 769 # check whether we already have this ref in the list 770 if not any(event_ref.is_equal(ref) for ref in self.event_ref_list): 771 self.event_ref_list.append(event_ref) 772 773 def get_event_ref_list(self): 774 """ 775 Return the list of :class:`~.eventref.EventRef` objects associated with 776 :class:`~.event.Event` instances. 777 778 :returns: Returns the list of :class:`~.eventref.EventRef` objects 779 associated with the Person instance. 780 :rtype: list 781 """ 782 return self.event_ref_list 783 784 def get_primary_event_ref_list(self): 785 """ 786 Return the list of :class:`~.eventref.EventRef` objects associated with 787 :class:`~.event.Event` instances that have been marked as primary 788 events. 789 790 :returns: Returns generator of :class:`~.eventref.EventRef` objects 791 associated with the Person instance. 792 :rtype: generator 793 """ 794 return (ref for ref in self.event_ref_list 795 if ref.get_role() == EventRoleType.PRIMARY 796 ) 797 798 def set_event_ref_list(self, event_ref_list): 799 """ 800 Set the Person instance's :class:`~.eventref.EventRef` list to the 801 passed list. 802 803 :param event_ref_list: List of valid :class:`~.eventref.EventRef` 804 objects. 805 :type event_ref_list: list 806 """ 807 self.event_ref_list = event_ref_list 808 809 def _merge_event_ref_list(self, acquisition): 810 """ 811 Merge the list of event references from acquisition with our own. 812 813 :param acquisition: the event references list of this object will be 814 merged with the current event references list. 815 :rtype acquisition: Person 816 """ 817 eventref_list = self.event_ref_list[:] 818 for idx, addendum in enumerate(acquisition.get_event_ref_list()): 819 for eventref in eventref_list: 820 equi = eventref.is_equivalent(addendum) 821 if equi == IDENTICAL: 822 break 823 elif equi == EQUAL: 824 eventref.merge(addendum) 825 break 826 else: 827 self.event_ref_list.append(addendum) 828 if (self.birth_ref_index == -1 and 829 idx == acquisition.birth_ref_index): 830 self.birth_ref_index = len(self.event_ref_list) - 1 831 if (self.death_ref_index == -1 and 832 idx == acquisition.death_ref_index): 833 self.death_ref_index = len(self.event_ref_list) - 1 834 835 def add_family_handle(self, family_handle): 836 """ 837 Add the :class:`~.family.Family` handle to the Person instance's 838 :class:`~.family.Family` list. 839 840 This is accomplished by assigning the handle of a valid 841 :class:`~.family.Family` in the current database. 842 843 Adding a :class:`~.family.Family` handle to a Person does not 844 automatically update the corresponding :class:`~.family.Family`. The 845 developer is responsible to make sure that when a 846 :class:`~.family.Family` is added to Person, that the Person is assigned 847 to either the father or mother role in the :class:`~.family.Family`. 848 849 :param family_handle: handle of the :class:`~.family.Family` to be added 850 to the Person's :class:`~.family.Family` list. 851 :type family_handle: str 852 """ 853 if family_handle not in self.family_list: 854 self.family_list.append(family_handle) 855 856 def set_preferred_family_handle(self, family_handle): 857 """ 858 Set the family_handle specified to be the preferred 859 :class:`~.family.Family`. 860 861 The preferred :class:`~.family.Family` is determined by the first 862 :class:`~.family.Family` in the :class:`~.family.Family` list, and is 863 typically used to indicate the preferred :class:`~.family.Family` for 864 navigation or reporting. 865 866 The family_handle must already be in the list, or the function 867 call has no effect. 868 869 :param family_handle: Handle of the :class:`~.family.Family` to make the 870 preferred :class:`~.family.Family`. 871 :type family_handle: str 872 :returns: True if the call succeeded, False if the family_handle 873 was not already in the :class:`~.family.Family` list. 874 :rtype: bool 875 """ 876 if family_handle in self.family_list: 877 self.family_list.remove(family_handle) 878 self.family_list = [family_handle] + self.family_list 879 return True 880 else: 881 return False 882 883 def get_family_handle_list(self): 884 """ 885 Return the list of :class:`~.family.Family` handles in which the person 886 is a parent or spouse. 887 888 :returns: Returns the list of handles corresponding to the 889 :class:`~.family.Family` records with which the person 890 is associated. 891 :rtype: list 892 """ 893 return self.family_list 894 895 def set_family_handle_list(self, family_list): 896 """ 897 Assign the passed list to the Person's list of families in which it is 898 a parent or spouse. 899 900 :param family_list: List of :class:`~.family.Family` handles to be 901 associated with the Person 902 :type family_list: list 903 """ 904 self.family_list = family_list 905 906 def clear_family_handle_list(self): 907 """ 908 Remove all :class:`~.family.Family` handles from the 909 :class:`~.family.Family` list. 910 """ 911 self.family_list = [] 912 913 def remove_family_handle(self, family_handle): 914 """ 915 Remove the specified :class:`~.family.Family` handle from the list of 916 marriages/partnerships. 917 918 If the handle does not exist in the list, the operation has no effect. 919 920 :param family_handle: :class:`~.family.Family` handle to remove from 921 the list 922 :type family_handle: str 923 924 :returns: True if the handle was removed, False if it was not 925 in the list. 926 :rtype: bool 927 """ 928 if family_handle in self.family_list: 929 self.family_list.remove(family_handle) 930 return True 931 else: 932 return False 933 934 def get_parent_family_handle_list(self): 935 """ 936 Return the list of :class:`~.family.Family` handles in which the person 937 is a child. 938 939 :returns: Returns the list of handles corresponding to the 940 :class:`~.family.Family` records with which the person is a 941 child. 942 :rtype: list 943 """ 944 return self.parent_family_list 945 946 def set_parent_family_handle_list(self, family_list): 947 """ 948 Return the list of :class:`~.family.Family` handles in which the person 949 is a child. 950 951 :returns: Returns the list of handles corresponding to the 952 :class:`~.family.Family` records with which the person is a 953 child. 954 :rtype: list 955 """ 956 self.parent_family_list = family_list 957 958 def add_parent_family_handle(self, family_handle): 959 """ 960 Add the :class:`~.family.Family` handle to the Person instance's list of 961 families in which it is a child. 962 963 This is accomplished by assigning the handle of a valid 964 :class:`~.family.Family` in the current database. 965 966 Adding a :class:`~.family.Family` handle to a Person does not 967 automatically update the corresponding :class:`~.family.Family`. The 968 developer is responsible to make sure that when a 969 :class:`~.family.Family` is added to Person, that the Person is 970 added to the :class:`~.family.Family` instance's child list. 971 972 :param family_handle: handle of the :class:`~.family.Family` to be added 973 to the Person's :class:`~.family.Family` list. 974 :type family_handle: str 975 """ 976 if not isinstance(family_handle, str): 977 raise ValueError("Expecting handle, obtained %s" % str(family_handle)) 978 if family_handle not in self.parent_family_list: 979 self.parent_family_list.append(family_handle) 980 981 def clear_parent_family_handle_list(self): 982 """ 983 Remove all :class:`~.family.Family` handles from the parent 984 :class:`~.family.Family` list. 985 """ 986 self.parent_family_list = [] 987 988 def remove_parent_family_handle(self, family_handle): 989 """ 990 Remove the specified :class:`~.family.Family` handle from the list of 991 parent families (families in which the parent is a child). 992 993 If the handle does not exist in the list, the operation has no effect. 994 995 :param family_handle: :class:`~.family.Family` handle to remove from the 996 list 997 :type family_handle: str 998 999 :returns: Returns a tuple of three strings, consisting of the 1000 removed handle, relationship to mother, and relationship 1001 to father. None is returned if the handle is not in the 1002 list. 1003 :rtype: tuple 1004 """ 1005 if family_handle in self.parent_family_list: 1006 self.parent_family_list.remove(family_handle) 1007 return True 1008 else: 1009 return False 1010 1011 def set_main_parent_family_handle(self, family_handle): 1012 """ 1013 Set the main :class:`~.family.Family` in which the Person is a child. 1014 1015 The main :class:`~.family.Family` is the :class:`~.family.Family` 1016 typically used for reports and navigation. This is accomplished by 1017 moving the :class:`~.family.Family` to the beginning of the list. The 1018 family_handle must be in the list for this to have any effect. 1019 1020 :param family_handle: handle of the :class:`~.family.Family` to be 1021 marked as the main :class:`~.family.Family` 1022 :type family_handle: str 1023 :returns: Returns True if the assignment has successful 1024 :rtype: bool 1025 """ 1026 if family_handle in self.parent_family_list: 1027 self.parent_family_list.remove(family_handle) 1028 self.parent_family_list = [family_handle] + self.parent_family_list 1029 return True 1030 else: 1031 return False 1032 1033 def get_main_parents_family_handle(self): 1034 """ 1035 Return the handle of the :class:`~.family.Family` considered to be the 1036 main :class:`~.family.Family` in which the Person is a child. 1037 1038 :returns: Returns the family_handle if a family_handle exists, 1039 If no :class:`~.family.Family` is assigned, None is returned 1040 :rtype: str 1041 """ 1042 if self.parent_family_list: 1043 return self.parent_family_list[0] 1044 else: 1045 return None 1046 1047 def add_person_ref(self, person_ref): 1048 """ 1049 Add the :class:`~.personref.PersonRef` to the Person instance's 1050 :class:`~.personref.PersonRef` list. 1051 1052 :param person_ref: the :class:`~.personref.PersonRef` to be added to the 1053 Person's :class:`~.personref.PersonRef` list. 1054 :type person_ref: PersonRef 1055 """ 1056 if person_ref and not isinstance(person_ref, PersonRef): 1057 raise ValueError("Expecting PersonRef instance") 1058 self.person_ref_list.append(person_ref) 1059 1060 def get_person_ref_list(self): 1061 """ 1062 Return the list of :class:`~.personref.PersonRef` objects. 1063 1064 :returns: Returns the list of :class:`~.personref.PersonRef` objects. 1065 :rtype: list 1066 """ 1067 return self.person_ref_list 1068 1069 def set_person_ref_list(self, person_ref_list): 1070 """ 1071 Set the Person instance's :class:`~.personref.PersonRef` list to the 1072 passed list. 1073 1074 :param person_ref_list: List of valid :class:`~.personref.PersonRef` 1075 objects 1076 :type person_ref_list: list 1077 """ 1078 self.person_ref_list = person_ref_list 1079 1080 def _merge_person_ref_list(self, acquisition): 1081 """ 1082 Merge the list of person references from acquisition with our own. 1083 1084 :param acquisition: the list of person references of this person will be 1085 merged with the current person references list. 1086 :rtype acquisition: Person 1087 """ 1088 personref_list = self.person_ref_list[:] 1089 for addendum in acquisition.get_person_ref_list(): 1090 for personref in personref_list: 1091 equi = personref.is_equivalent(addendum) 1092 if equi == IDENTICAL: 1093 break 1094 elif equi == EQUAL: 1095 personref.merge(addendum) 1096 break 1097 else: 1098 self.person_ref_list.append(addendum) 1099