1#
2# Gramps - a GTK+/GNOME based genealogy program
3#
4# Copyright (C) 2000-2007  Donald N. Allingham
5# Copyright (C) 2010       Nick Hall
6# Copyright (C) 2011       Tim G L Lyons
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22
23"""
24Base class for the Gramps databases. All database interfaces should inherit
25from this class.
26"""
27
28#-------------------------------------------------------------------------
29#
30# Python libraries
31#
32#-------------------------------------------------------------------------
33import re
34import time
35from operator import itemgetter
36import logging
37
38#-------------------------------------------------------------------------
39#
40# Gramps libraries
41#
42#-------------------------------------------------------------------------
43from ..db.dbconst import DBLOGNAME
44from ..const import GRAMPS_LOCALE as glocale
45_ = glocale.translation.gettext
46from ..lib.childreftype import ChildRefType
47from ..lib.childref import ChildRef
48from .txn import DbTxn
49from .exceptions import DbTransactionCancel, DbException
50
51_LOG = logging.getLogger(DBLOGNAME)
52
53#-------------------------------------------------------------------------
54#
55# Gramps libraries
56#
57#-------------------------------------------------------------------------
58
59
60class DbReadBase:
61    """
62    Gramps database object. This object is a base class for all
63    database interfaces.  All methods raise NotImplementedError
64    and must be implemented in the derived class as required.
65    """
66
67    def __init__(self):
68        """
69        Create a new DbReadBase instance.
70
71        A new DbReadBase class should never be directly created. Only classes
72        derived from this class should be created.
73        """
74        self.basedb = self
75        self.__feature = {} # {"feature": VALUE, ...}
76
77    def get_feature(self, feature):
78        """
79        Databases can implement certain features or not. The default is
80        None, unless otherwise explicitly stated.
81        """
82        return self.__feature.get(feature, None) # can also be explicitly None
83
84    def set_feature(self, feature, value):
85        """
86        Databases can implement certain features.
87        """
88        self.__feature[feature] = value
89
90    def close(self):
91        """
92        Close the specified database.
93        """
94        raise NotImplementedError
95
96    def db_has_bm_changes(self):
97        """
98        Return whethere there were bookmark changes during the session.
99        """
100        raise NotImplementedError
101
102    def find_backlink_handles(self, handle, include_classes=None):
103        """
104        Find all objects that hold a reference to the object handle.
105
106        Returns an iterator over a list of (class_name, handle) tuples.
107
108        :param handle: handle of the object to search for.
109        :type handle: str database handle
110        :param include_classes: list of class names to include in the results.
111            Default is None which includes all classes.
112        :type include_classes: list of class names
113
114        This default implementation does a sequential scan through all
115        the primary object databases and is very slow. Backends can
116        override this method to provide much faster implementations that
117        make use of additional capabilities of the backend.
118
119        Note that this is a generator function, it returns a iterator for
120        use in loops. If you want a list of the results use::
121
122            result_list = list(find_backlink_handles(handle))
123        """
124        raise NotImplementedError
125
126    def find_initial_person(self):
127        """
128        Returns first person in the database
129        """
130        raise NotImplementedError
131
132    def get_child_reference_types(self):
133        """
134        Return a list of all child reference types associated with Family
135        instances in the database.
136        """
137        raise NotImplementedError
138
139    def get_default_handle(self):
140        """
141        Return the default Person of the database.
142        """
143        raise NotImplementedError
144
145    def get_default_person(self):
146        """
147        Return the default Person of the database.
148        """
149        raise NotImplementedError
150
151    def find_next_citation_gramps_id(self):
152        """
153        Return the next available Gramps ID for a Event object based off the
154        event ID prefix.
155        """
156        raise NotImplementedError
157
158    def find_next_event_gramps_id(self):
159        """
160        Return the next available Gramps ID for a Event object based off the
161        event ID prefix.
162        """
163        raise NotImplementedError
164
165    def find_next_family_gramps_id(self):
166        """
167        Return the next available Gramps ID for a Family object based off the
168        family ID prefix.
169        """
170        raise NotImplementedError
171
172    def find_next_media_gramps_id(self):
173        """
174        Return the next available Gramps ID for a Media object based
175        off the media object ID prefix.
176        """
177        raise NotImplementedError
178
179    def find_next_note_gramps_id(self):
180        """
181        Return the next available Gramps ID for a Note object based off the
182        note ID prefix.
183        """
184        raise NotImplementedError
185
186    def find_next_person_gramps_id(self):
187        """
188        Return the next available Gramps ID for a Person object based off the
189        person ID prefix.
190        """
191        raise NotImplementedError
192
193    def find_next_place_gramps_id(self):
194        """
195        Return the next available Gramps ID for a Place object based off the
196        place ID prefix.
197        """
198        raise NotImplementedError
199
200    def find_next_repository_gramps_id(self):
201        """
202        Return the next available Gramps ID for a Repository object based
203        off the repository ID prefix.
204        """
205        raise NotImplementedError
206
207    def find_next_source_gramps_id(self):
208        """
209        Return the next available Gramps ID for a Source object based off the
210        source ID prefix.
211        """
212        raise NotImplementedError
213
214    def get_bookmarks(self):
215        """
216        Return the list of Person handles in the bookmarks.
217        """
218        raise NotImplementedError
219
220    def get_citation_bookmarks(self):
221        """
222        Return the list of Citation handles in the bookmarks.
223        """
224        raise NotImplementedError
225
226    def get_event_bookmarks(self):
227        """
228        Return the list of Event handles in the bookmarks.
229        """
230        raise NotImplementedError
231
232    def get_family_bookmarks(self):
233        """
234        Return the list of Family handles in the bookmarks.
235        """
236        raise NotImplementedError
237
238    def get_media_bookmarks(self):
239        """
240        Return the list of Media handles in the bookmarks.
241        """
242        raise NotImplementedError
243
244    def get_note_bookmarks(self):
245        """
246        Return the list of Note handles in the bookmarks.
247        """
248        raise NotImplementedError
249
250    def get_place_bookmarks(self):
251        """
252        Return the list of Place handles in the bookmarks.
253        """
254        raise NotImplementedError
255
256    def get_repo_bookmarks(self):
257        """
258        Return the list of Repository handles in the bookmarks.
259        """
260        raise NotImplementedError
261
262    def get_source_bookmarks(self):
263        """
264        Return the list of Source handles in the bookmarks.
265        """
266        raise NotImplementedError
267
268    def get_citation_cursor(self):
269        """
270        Return a reference to a cursor over Citation objects.  Example use::
271
272            with get_citation_cursor() as cursor:
273                for handle, citation in cursor:
274                    # process citation object pointed to by the handle
275        """
276        raise NotImplementedError
277
278    def get_event_cursor(self):
279        """
280        Return a reference to a cursor over Family objects.  Example use::
281
282            with get_event_cursor() as cursor:
283                for handle, event in cursor:
284                    # process event object pointed to by the handle
285        """
286        raise NotImplementedError
287
288    def get_family_cursor(self):
289        """
290        Return a reference to a cursor over Family objects.  Example use::
291
292            with get_family_cursor() as cursor:
293                for handle, family in cursor:
294                    # process family object pointed to by the handle
295        """
296        raise NotImplementedError
297
298    def get_media_cursor(self):
299        """
300        Return a reference to a cursor over Media objects.  Example use::
301
302            with get_media_cursor() as cursor:
303                for handle, media in cursor:
304                    # process media object pointed to by the handle
305        """
306        raise NotImplementedError
307
308    def get_note_cursor(self):
309        """
310        Return a reference to a cursor over Note objects.  Example use::
311
312            with get_note_cursor() as cursor:
313                for handle, note in cursor:
314                    # process note object pointed to by the handle
315        """
316        raise NotImplementedError
317
318    def get_person_cursor(self):
319        """
320        Return a reference to a cursor over Person objects.  Example use::
321
322            with get_person_cursor() as cursor:
323                for handle, person in cursor:
324                    # process person object pointed to by the handle
325        """
326        raise NotImplementedError
327
328    def get_place_cursor(self):
329        """
330        Return a reference to a cursor over Place objects.  Example use::
331
332            with get_place_cursor() as cursor:
333                for handle, place in cursor:
334                    # process place object pointed to by the handle
335        """
336        raise NotImplementedError
337
338    def get_place_tree_cursor(self):
339        """
340        Return a reference to a cursor that iterates over Place objects in the
341        order they appear in the place hierarchy.  Example use::
342
343            with get_place_tree_cursor() as cursor:
344                for handle, place in cursor:
345                    # process place object pointed to by the handle
346        """
347        raise NotImplementedError
348
349    def get_repository_cursor(self):
350        """
351        Return a reference to a cursor over Repository objects.  Example use::
352
353            with get_repository_cursor() as cursor:
354                for handle, repository in cursor:
355                    # process repository object pointed to by the handle
356        """
357        raise NotImplementedError
358
359    def get_source_cursor(self):
360        """
361        Return a reference to a cursor over Source objects.  Example use::
362
363            with get_source_cursor() as cursor:
364                for handle, source in cursor:
365                    # process source object pointed to by the handle
366        """
367        raise NotImplementedError
368
369    def get_tag_cursor(self):
370        """
371        Return a reference to a cursor over Tag objects.  Example use::
372
373            with get_tag_cursor() as cursor:
374                for handle, tag in cursor:
375                    # process tag object pointed to by the handle
376        """
377        raise NotImplementedError
378
379    def get_citation_from_gramps_id(self, val):
380        """
381        Find a Citation in the database from the passed Gramps ID.
382
383        :param val: gramps_id of the object to search for.
384        :type val: str or bytes
385
386        If no such Citation exists, None is returned.
387        """
388        raise NotImplementedError
389
390    def get_event_from_gramps_id(self, val):
391        """
392        Find an Event in the database from the passed Gramps ID.
393
394        :param val: gramps_id of the object to search for.
395        :type val: str or bytes
396
397        If no such Event exists, None is returned.
398        """
399        raise NotImplementedError
400
401    def get_family_from_gramps_id(self, val):
402        """
403        Find a Family in the database from the passed Gramps ID.
404
405        :param val: gramps_id of the object to search for.
406        :type val: str or bytes
407
408        If no such Family exists, None is returned.
409        """
410        raise NotImplementedError
411
412    def get_media_from_gramps_id(self, val):
413        """
414        Find a Media in the database from the passed Gramps ID.
415
416        :param val: gramps_id of the object to search for.
417        :type val: str or bytes
418
419        If no such Media exists, None is returned.
420        """
421        raise NotImplementedError
422
423    def get_note_from_gramps_id(self, val):
424        """
425        Find a Note in the database from the passed Gramps ID.
426
427        :param val: gramps_id of the object to search for.
428        :type val: str or bytes
429
430        If no such Note exists, None is returned.
431        """
432        raise NotImplementedError
433
434    def get_person_from_gramps_id(self, val):
435        """
436        Find a Person in the database from the passed Gramps ID.
437
438        :param val: gramps_id of the object to search for.
439        :type val: str or bytes
440
441        If no such Person exists, None is returned.
442        """
443        raise NotImplementedError
444
445    def get_place_from_gramps_id(self, val):
446        """
447        Find a Place in the database from the passed Gramps ID.
448
449        :param val: gramps_id of the object to search for.
450        :type val: str or bytes
451
452        If no such Place exists, None is returned.
453        """
454        raise NotImplementedError
455
456    def get_repository_from_gramps_id(self, val):
457        """
458        Find a Repository in the database from the passed Gramps ID.
459
460        :param val: gramps_id of the object to search for.
461        :type val: str or bytes
462
463        If no such Repository exists, None is returned.
464        """
465        raise NotImplementedError
466
467    def get_source_from_gramps_id(self, val):
468        """
469        Find a Source in the database from the passed Gramps ID.
470
471        :param val: gramps_id of the object to search for.
472        :type val: str or bytes
473
474        If no such Source exists, None is returned.
475        """
476        raise NotImplementedError
477
478    def get_citation_from_handle(self, handle):
479        """
480        Return a Citation in the database from the passed handle.
481
482        :param handle: handle of the object to search for.
483        :type handle: str or bytes
484
485        If no such Citation exists, a HandleError is raised.
486        Note: if used through a proxy (Filter for reports etc.) a 'None' is
487        returned in cases where the Citation is filtered out.
488        """
489        raise NotImplementedError
490
491    def get_event_from_handle(self, handle):
492        """
493        Return an Event in the database from the passed handle.
494
495        :param handle: handle of the object to search for.
496        :type handle: str or bytes
497
498        If no such Event exists, a HandleError is raised.
499        Note: if used through a proxy (Filter for reports etc.) a 'None' is
500        returned in cases where the Event is filtered out.
501        """
502        raise NotImplementedError
503
504    def get_family_from_handle(self, handle):
505        """
506        Return a Family in the database from the passed handle.
507
508        :param handle: handle of the object to search for.
509        :type handle: str or bytes
510
511        If no such Family exists, a HandleError is raised.
512        Note: if used through a proxy (Filter for reports etc.) a 'None' is
513        returned in cases where the Family is filtered out.
514        """
515        raise NotImplementedError
516
517    def get_media_from_handle(self, handle):
518        """
519        Return a Media in the database from the passed handle.
520
521        :param handle: handle of the object to search for.
522        :type handle: str or bytes
523
524        If no such Object exists, a HandleError is raised.
525        Note: if used through a proxy (Filter for reports etc.) a 'None' is
526        returned in cases where the Media is filtered out.
527        """
528        raise NotImplementedError
529
530    def get_note_from_handle(self, handle):
531        """
532        Return a Note in the database from the passed handle.
533
534        :param handle: handle of the object to search for.
535        :type handle: str or bytes
536
537        If no such Note exists, a HandleError is raised.
538        Note: if used through a proxy (Filter for reports etc.) a 'None' is
539        returned in cases where the Note is filtered out.
540        """
541        raise NotImplementedError
542
543    def get_person_from_handle(self, handle):
544        """
545        Return a Person in the database from the passed handle.
546
547        :param handle: handle of the object to search for.
548        :type handle: str or bytes
549
550        If no such Person exists, a HandleError is raised.
551        Note: if used through a proxy (Filter for reports etc.) a 'None' is
552        returned in cases where the Person is filtered out.
553        """
554        raise NotImplementedError
555
556    def get_place_from_handle(self, handle):
557        """
558        Return a Place in the database from the passed handle.
559
560        :param handle: handle of the object to search for.
561        :type handle: str or bytes
562
563        If no such Place exists, a HandleError is raised.
564        Note: if used through a proxy (Filter for reports etc.) a 'None' is
565        returned in cases where the Place is filtered out.
566        """
567        raise NotImplementedError
568
569    def get_repository_from_handle(self, handle):
570        """
571        Return a Repository in the database from the passed handle.
572
573        :param handle: handle of the object to search for.
574        :type handle: str or bytes
575
576        If no such Repository exists, a HandleError is raised.
577        Note: if used through a proxy (Filter for reports etc.) a 'None' is
578        returned in cases where the Repository is filtered out.
579        """
580        raise NotImplementedError
581
582    def get_source_from_handle(self, handle):
583        """
584        Return a Source in the database from the passed handle.
585
586        :param handle: handle of the object to search for.
587        :type handle: str or bytes
588
589        If no such Source exists, a HandleError is raised.
590        Note: if used through a proxy (Filter for reports etc.) a 'None' is
591        returned in cases where the Source is filtered out.
592        """
593        raise NotImplementedError
594
595    def get_tag_from_handle(self, handle):
596        """
597        Return a Tag in the database from the passed handle.
598
599        :param handle: handle of the object to search for.
600        :type handle: str or bytes
601
602        If no such Tag exists, a HandleError is raised.
603        Note: if used through a proxy (Filter for reports etc.) a 'None' is
604        returned in cases where the Tag is filtered out.
605        """
606        raise NotImplementedError
607
608    def get_citation_handles(self, sort_handles=False, locale=glocale):
609        """
610        Return a list of database handles, one handle for each Citation in
611        the database.
612
613        :param sort_handles: If True, the list is sorted by Citation title.
614        :type sort_handles: bool
615        :param locale: The locale to use for collation.
616        :type locale: A GrampsLocale object.
617        """
618        raise NotImplementedError
619
620    def get_event_handles(self):
621        """
622        Return a list of database handles, one handle for each Event in the
623        database.
624
625        .. warning:: For speed the keys are directly returned, so handles are
626                     bytes type
627        """
628        raise NotImplementedError
629
630    def get_family_handles(self, sort_handles=False, locale=glocale):
631        """
632        Return a list of database handles, one handle for each Family in
633        the database.
634
635        :param sort_handles: If True, the list is sorted by surnames.
636        :type sort_handles: bool
637        :param locale: The locale to use for collation.
638        :type locale: A GrampsLocale object.
639
640        .. warning:: For speed the keys are directly returned, so handles are
641                     bytes type
642        """
643        raise NotImplementedError
644
645    def get_media_handles(self, sort_handles=False, locale=glocale):
646        """
647        Return a list of database handles, one handle for each Media in
648        the database.
649
650        :param sort_handles: If True, the list is sorted by title.
651        :type sort_handles: bool
652        :param locale: The locale to use for collation.
653        :type locale: A GrampsLocale object.
654
655        .. warning:: For speed the keys are directly returned, so handles are
656                     bytes type
657        """
658        raise NotImplementedError
659
660    def get_note_handles(self):
661        """
662        Return a list of database handles, one handle for each Note in the
663        database.
664
665        .. warning:: For speed the keys are directly returned, so handles are
666                     bytes type
667        """
668        raise NotImplementedError
669
670    def get_person_handles(self, sort_handles=False, locale=glocale):
671        """
672        Return a list of database handles, one handle for each Person in
673        the database.
674
675        :param sort_handles: If True, the list is sorted by surnames.
676        :type sort_handles: bool
677        :param locale: The locale to use for collation.
678        :type locale: A GrampsLocale object.
679
680        .. warning:: For speed the keys are directly returned, so handles are
681                     bytes type
682        """
683        raise NotImplementedError
684
685    def get_place_handles(self, sort_handles=False, locale=glocale):
686        """
687        Return a list of database handles, one handle for each Place in
688        the database.
689
690        :param sort_handles: If True, the list is sorted by Place title.
691        :type sort_handles: bool
692        :param locale: The locale to use for collation.
693        :type locale: A GrampsLocale object.
694
695        .. warning:: For speed the keys are directly returned, so handles are
696                     bytes type
697        """
698        raise NotImplementedError
699
700    def get_repository_handles(self):
701        """
702        Return a list of database handles, one handle for each Repository in
703        the database.
704
705        .. warning:: For speed the keys are directly returned, so handles are
706                     bytes type
707        """
708        raise NotImplementedError
709
710    def get_source_handles(self, sort_handles=False, locale=glocale):
711        """
712        Return a list of database handles, one handle for each Source in
713        the database.
714
715        :param sort_handles: If True, the list is sorted by Source title.
716        :type sort_handles: bool
717        :param locale: The locale to use for collation.
718        :type locale: A GrampsLocale object.
719
720        .. warning:: For speed the keys are directly returned, so handles are
721                     bytes type
722        """
723        raise NotImplementedError
724
725    def get_tag_handles(self, sort_handles=False, locale=glocale):
726        """
727        Return a list of database handles, one handle for each Tag in
728        the database.
729
730        :param sort_handles: If True, the list is sorted by Tag name.
731        :type sort_handles: bool
732        :param locale: The locale to use for collation.
733        :type locale: A GrampsLocale object.
734
735        .. warning:: For speed the keys are directly returned, so handles are
736                     bytes type
737        """
738        raise NotImplementedError
739
740    def get_event_roles(self):
741        """
742        Return a list of all custom event role names associated with Event
743        instances in the database.
744        """
745        raise NotImplementedError
746
747    def get_event_attribute_types(self):
748        """
749        Return a list of all Attribute types assocated with Event instances
750        in the database.
751        """
752        raise NotImplementedError
753
754    def get_family_attribute_types(self):
755        """
756        Return a list of all Attribute types associated with Family instances
757        in the database.
758        """
759        raise NotImplementedError
760
761    def get_media_attribute_types(self):
762        """
763        Return a list of all Attribute types associated with Media and MediaRef
764        instances in the database.
765        """
766        raise NotImplementedError
767
768    def get_person_attribute_types(self):
769        """
770        Return a list of all Attribute types associated with Person instances
771        in the database.
772        """
773        raise NotImplementedError
774
775    def get_source_attribute_types(self):
776        """
777        Return a list of all Attribute types associated with Source/Citation
778        instances in the database.
779        """
780        raise NotImplementedError
781
782    def get_event_types(self):
783        """
784        Return a list of all event types in the database.
785        """
786        raise NotImplementedError
787
788    def get_family_event_types(self):
789        """
790        Deprecated:  Use get_event_types
791        """
792        raise NotImplementedError
793
794    def get_family_relation_types(self):
795        """
796        Return a list of all relationship types associated with Family
797        instances in the database.
798        """
799        raise NotImplementedError
800
801    def get_name_types(self):
802        """
803        Return a list of all custom names types associated with Person
804        instances in the database.
805        """
806        raise NotImplementedError
807
808    def get_note_types(self):
809        """
810        Return a list of all custom note types associated with Note instances
811        in the database.
812        """
813        raise NotImplementedError
814
815    def get_origin_types(self):
816        """
817        Return a list of all custom origin types associated with Person/Surname
818        instances in the database.
819        """
820        raise NotImplementedError
821
822    def get_place_types(self):
823        """
824        Return a list of all custom place types assocated with Place instances
825        in the database.
826        """
827        raise NotImplementedError
828
829    def get_repository_types(self):
830        """
831        Return a list of all custom repository types associated with Repository
832        instances in the database.
833        """
834        raise NotImplementedError
835
836    def get_source_media_types(self):
837        """
838        Return a list of all custom source media types associated with Source
839        instances in the database.
840        """
841        raise NotImplementedError
842
843    def get_url_types(self):
844        """
845        Return a list of all custom names types associated with Url instances
846        in the database.
847        """
848        raise NotImplementedError
849
850    def get_mediapath(self):
851        """
852        Return the default media path of the database.
853        """
854        raise NotImplementedError
855
856    def get_name_group_keys(self):
857        """
858        Return the defined names that have been assigned to a default grouping.
859        """
860        raise NotImplementedError
861
862    def get_name_group_mapping(self, surname):
863        """
864        Return the default grouping name for a surname.
865        """
866        raise NotImplementedError
867
868    def get_number_of_citations(self):
869        """
870        Return the number of citations currently in the database.
871        """
872        raise NotImplementedError
873
874    def get_number_of_events(self):
875        """
876        Return the number of events currently in the database.
877        """
878        raise NotImplementedError
879
880    def get_number_of_families(self):
881        """
882        Return the number of families currently in the database.
883        """
884        raise NotImplementedError
885
886    def get_number_of_media(self):
887        """
888        Return the number of media objects currently in the database.
889        """
890        raise NotImplementedError
891
892    def get_number_of_notes(self):
893        """
894        Return the number of notes currently in the database.
895        """
896        raise NotImplementedError
897
898    def get_number_of_people(self):
899        """
900        Return the number of people currently in the database.
901        """
902        raise NotImplementedError
903
904    def get_number_of_places(self):
905        """
906        Return the number of places currently in the database.
907        """
908        raise NotImplementedError
909
910    def get_number_of_repositories(self):
911        """
912        Return the number of source repositories currently in the database.
913        """
914        raise NotImplementedError
915
916    def get_number_of_sources(self):
917        """
918        Return the number of sources currently in the database.
919        """
920        raise NotImplementedError
921
922    def get_number_of_tags(self):
923        """
924        Return the number of tags currently in the database.
925        """
926        raise NotImplementedError
927
928    def get_person_event_types(self):
929        """
930        Deprecated:  Use get_event_types
931        """
932        raise NotImplementedError
933
934    def get_raw_citation_data(self, handle):
935        """
936        Return raw (serialized and pickled) Citation object from handle
937        """
938        raise NotImplementedError
939
940    def get_raw_event_data(self, handle):
941        """
942        Return raw (serialized and pickled) Event object from handle
943        """
944        raise NotImplementedError
945
946    def get_raw_family_data(self, handle):
947        """
948        Return raw (serialized and pickled) Family object from handle
949        """
950        raise NotImplementedError
951
952    def get_raw_media_data(self, handle):
953        """
954        Return raw (serialized and pickled) Family object from handle
955        """
956        raise NotImplementedError
957
958    def get_raw_note_data(self, handle):
959        """
960        Return raw (serialized and pickled) Note object from handle
961        """
962        raise NotImplementedError
963
964    def get_raw_person_data(self, handle):
965        """
966        Return raw (serialized and pickled) Person object from handle
967        """
968        raise NotImplementedError
969
970    def get_raw_place_data(self, handle):
971        """
972        Return raw (serialized and pickled) Place object from handle
973        """
974        raise NotImplementedError
975
976    def get_raw_repository_data(self, handle):
977        """
978        Return raw (serialized and pickled) Repository object from handle
979        """
980        raise NotImplementedError
981
982    def get_raw_source_data(self, handle):
983        """
984        Return raw (serialized and pickled) Source object from handle
985        """
986        raise NotImplementedError
987
988    def get_raw_tag_data(self, handle):
989        """
990        Return raw (serialized and pickled) Tag object from handle
991        """
992        raise NotImplementedError
993
994    def get_researcher(self):
995        """
996        Return the Researcher instance, providing information about the owner
997        of the database.
998        """
999        raise NotImplementedError
1000
1001    def get_save_path(self):
1002        """
1003        Return the save path of the file, or "" if one does not exist.
1004        """
1005        raise NotImplementedError
1006
1007    def get_surname_list(self):
1008        """
1009        Return the list of locale-sorted surnames contained in the database.
1010        """
1011        raise NotImplementedError
1012
1013    def get_tag_from_name(self, val):
1014        """
1015        Find a Tag in the database from the passed Tag name.
1016
1017        If no such Tag exists, None is returned.
1018        """
1019        raise NotImplementedError
1020
1021    def has_citation_gramps_id(self, gramps_id):
1022        """
1023        Return True if the Gramps ID exists in the Citation table.
1024        """
1025        raise NotImplementedError
1026
1027    def has_event_gramps_id(self, gramps_id):
1028        """
1029        Return True if the Gramps ID exists in the Event table.
1030        """
1031        raise NotImplementedError
1032
1033    def has_family_gramps_id(self, gramps_id):
1034        """
1035        Return True if the Gramps ID exists in the Family table.
1036        """
1037        raise NotImplementedError
1038
1039    def has_media_gramps_id(self, gramps_id):
1040        """
1041        Return True if the Gramps ID exists in the Media table.
1042        """
1043        raise NotImplementedError
1044
1045    def has_note_gramps_id(self, gramps_id):
1046        """
1047        Return True if the Gramps ID exists in the Note table.
1048        """
1049        raise NotImplementedError
1050
1051    def has_person_gramps_id(self, gramps_id):
1052        """
1053        Return True if the Gramps ID exists in the Person table.
1054        """
1055        raise NotImplementedError
1056
1057    def has_place_gramps_id(self, gramps_id):
1058        """
1059        Return True if the Gramps ID exists in the Place table.
1060        """
1061        raise NotImplementedError
1062
1063    def has_repository_gramps_id(self, gramps_id):
1064        """
1065        Return True if the Gramps ID exists in the Repository table.
1066        """
1067        raise NotImplementedError
1068
1069    def has_source_gramps_id(self, gramps_id):
1070        """
1071        Return True if the Gramps ID exists in the Source table.
1072        """
1073        raise NotImplementedError
1074
1075    def has_event_handle(self, handle):
1076        """
1077        Return True if the handle exists in the current Event database.
1078        """
1079        raise NotImplementedError
1080
1081    def has_family_handle(self, handle):
1082        """
1083        Return True if the handle exists in the current Family database.
1084        """
1085        raise NotImplementedError
1086
1087    def has_media_handle(self, handle):
1088        """
1089        Return True if the handle exists in the current Mediadatabase.
1090        """
1091        raise NotImplementedError
1092
1093    def has_note_handle(self, handle):
1094        """
1095        Return True if the handle exists in the current Note database.
1096        """
1097        raise NotImplementedError
1098
1099    def has_person_handle(self, handle):
1100        """
1101        Return True if the handle exists in the current Person database.
1102        """
1103        raise NotImplementedError
1104
1105    def has_place_handle(self, handle):
1106        """
1107        Return True if the handle exists in the current Place database.
1108        """
1109        raise NotImplementedError
1110
1111    def has_repository_handle(self, handle):
1112        """
1113        Return True if the handle exists in the current Repository database.
1114        """
1115        raise NotImplementedError
1116
1117    def has_source_handle(self, handle):
1118        """
1119        Return True if the handle exists in the current Source database.
1120        """
1121        raise NotImplementedError
1122
1123    def has_citation_handle(self, handle):
1124        """
1125        Return True if the handle exists in the current Citation database.
1126        """
1127        raise NotImplementedError
1128
1129    def has_tag_handle(self, handle):
1130        """
1131        Return True if the handle exists in the current Tag database.
1132        """
1133        raise NotImplementedError
1134
1135    def has_name_group_key(self, name):
1136        """
1137        Return if a key exists in the name_group table.
1138        """
1139        raise NotImplementedError
1140
1141    def is_open(self):
1142        """
1143        Return True if the database has been opened.
1144        """
1145        raise NotImplementedError
1146
1147    def iter_citations(self):
1148        """
1149        Return an iterator over objects for Citations in the database
1150        """
1151        raise NotImplementedError
1152
1153    def iter_events(self):
1154        """
1155        Return an iterator over objects for Events in the database
1156        """
1157        raise NotImplementedError
1158
1159    def iter_families(self):
1160        """
1161        Return an iterator over objects for Families in the database
1162        """
1163        raise NotImplementedError
1164
1165    def iter_media(self):
1166        """
1167        Return an iterator over objects for Medias in the database
1168        """
1169        raise NotImplementedError
1170
1171    def iter_notes(self):
1172        """
1173        Return an iterator over objects for Notes in the database
1174        """
1175        raise NotImplementedError
1176
1177    def iter_people(self):
1178        """
1179        Return an iterator over objects for Persons in the database
1180        """
1181        raise NotImplementedError
1182
1183    def iter_places(self):
1184        """
1185        Return an iterator over objects for Places in the database
1186        """
1187        raise NotImplementedError
1188
1189    def iter_repositories(self):
1190        """
1191        Return an iterator over objects for Repositories in the database
1192        """
1193        raise NotImplementedError
1194
1195    def iter_sources(self):
1196        """
1197        Return an iterator over objects for Sources in the database
1198        """
1199        raise NotImplementedError
1200
1201    def iter_tags(self):
1202        """
1203        Return an iterator over objects for Tags in the database
1204        """
1205        raise NotImplementedError
1206
1207    def iter_citation_handles(self):
1208        """
1209        Return an iterator over handles for Citations in the database
1210        """
1211        raise NotImplementedError
1212
1213    def iter_event_handles(self):
1214        """
1215        Return an iterator over handles for Events in the database
1216        """
1217        raise NotImplementedError
1218
1219    def iter_family_handles(self):
1220        """
1221        Return an iterator over handles for Families in the database
1222        """
1223        raise NotImplementedError
1224
1225    def iter_media_handles(self):
1226        """
1227        Return an iterator over handles for Media in the database
1228        """
1229        raise NotImplementedError
1230
1231    def iter_note_handles(self):
1232        """
1233        Return an iterator over handles for Notes in the database
1234        """
1235        raise NotImplementedError
1236
1237    def iter_person_handles(self):
1238        """
1239        Return an iterator over handles for Persons in the database
1240        """
1241        raise NotImplementedError
1242
1243    def iter_place_handles(self):
1244        """
1245        Return an iterator over handles for Places in the database
1246        """
1247        raise NotImplementedError
1248
1249    def iter_repository_handles(self):
1250        """
1251        Return an iterator over handles for Repositories in the database
1252        """
1253        raise NotImplementedError
1254
1255    def iter_source_handles(self):
1256        """
1257        Return an iterator over handles for Sources in the database
1258        """
1259        raise NotImplementedError
1260
1261    def iter_tag_handles(self):
1262        """
1263        Return an iterator over handles for Tags in the database
1264        """
1265        raise NotImplementedError
1266
1267    def load(self, name, callback, mode=None, force_schema_upgrade=False,
1268             force_bsddb_upgrade=False):
1269        """
1270        Open the specified database.
1271        """
1272        raise NotImplementedError
1273
1274    def report_bm_change(self):
1275        """
1276        Add 1 to the number of bookmark changes during this session.
1277        """
1278        raise NotImplementedError
1279
1280    def request_rebuild(self):
1281        """
1282        Notify clients that the data has changed significantly, and that all
1283        internal data dependent on the database should be rebuilt.
1284        Note that all rebuild signals on all objects are emitted at the same
1285        time. It is correct to assume that this is always the case.
1286
1287        .. todo:: it might be better to replace these rebuild signals by one
1288                  single database-rebuild signal.
1289        """
1290        raise NotImplementedError
1291
1292    def version_supported(self):
1293        """
1294        Return True when the file has a supported version.
1295        """
1296        raise NotImplementedError
1297
1298    def set_prefixes(self, person, media, family, source, citation,
1299                     place, event, repository, note):
1300        """
1301        Set the prefixes for the gramps ids for all gramps objects
1302        """
1303        raise NotImplementedError
1304
1305    def set_citation_id_prefix(self, val):
1306        """
1307        Set the naming template for Gramps Citation ID values.
1308
1309        The string is expected to be in the form of a simple text string, or
1310        in a format that contains a C/Python style format string using %d,
1311        such as C%d or C%04d.
1312        """
1313        raise NotImplementedError
1314
1315    def set_event_id_prefix(self, val):
1316        """
1317        Set the naming template for Gramps Event ID values.
1318
1319        The string is expected to be in the form of a simple text string, or
1320        in a format that contains a C/Python style format string using %d,
1321        such as E%d or E%04d.
1322        """
1323        raise NotImplementedError
1324
1325    def set_family_id_prefix(self, val):
1326        """
1327        Set the naming template for Gramps Family ID values.
1328
1329        The string is expected to be in the form of a simple text string, or
1330        in a format that contains a C/Python style format string using %d,
1331        such as F%d or F%04d.
1332        """
1333        raise NotImplementedError
1334
1335    def set_note_id_prefix(self, val):
1336        """
1337        Set the naming template for Gramps Note ID values.
1338
1339        The string is expected to be in the form of a simple text string, or
1340        in a format that contains a C/Python style format string using %d,
1341        such as N%d or N%04d.
1342        """
1343        raise NotImplementedError
1344
1345    def set_media_id_prefix(self, val):
1346        """
1347        Set the naming template for Gramps Media ID values.
1348
1349        The string is expected to be in the form of a simple text string, or
1350        in a format that contains a C/Python style format string using %d,
1351        such as O%d or O%04d.
1352        """
1353        raise NotImplementedError
1354
1355    def set_person_id_prefix(self, val):
1356        """
1357        Set the naming template for Gramps Person ID values.
1358
1359        The string is expected to be in the form of a simple text string, or
1360        in a format that contains a C/Python style format string using %d,
1361        such as I%d or I%04d.
1362        """
1363        raise NotImplementedError
1364
1365    def set_place_id_prefix(self, val):
1366        """
1367        Set the naming template for Gramps Place ID values.
1368
1369        The string is expected to be in the form of a simple text string, or
1370        in a format that contains a C/Python style format string using %d,
1371        such as P%d or P%04d.
1372        """
1373        raise NotImplementedError
1374
1375    def set_repository_id_prefix(self, val):
1376        """
1377        Set the naming template for Gramps Repository ID values.
1378
1379        The string is expected to be in the form of a simple text string, or
1380        in a format that contains a C/Python style format string using %d,
1381        such as R%d or R%04d.
1382        """
1383        raise NotImplementedError
1384
1385    def set_source_id_prefix(self, val):
1386        """
1387        Set the naming template for Gramps Source ID values.
1388
1389        The string is expected to be in the form of a simple text string, or
1390        in a format that contains a C/Python style format string using %d,
1391        such as S%d or S%04d.
1392        """
1393        raise NotImplementedError
1394
1395    def set_mediapath(self, path):
1396        """
1397        Set the default media path for database.
1398        """
1399        raise NotImplementedError
1400
1401    def set_researcher(self, owner):
1402        """
1403        Set the information about the owner of the database.
1404        """
1405        raise NotImplementedError
1406
1407    def get_dbid(self):
1408        """
1409        A unique ID for this database on this computer.
1410        """
1411        raise NotImplementedError
1412
1413    def get_dbname(self):
1414        """
1415        A name for this database on this computer.
1416        """
1417        raise NotImplementedError
1418
1419    def get_summary(self):
1420        """
1421        Returns dictionary of summary item.
1422        Should include, if possible:
1423
1424        _("Number of people")
1425        _("Version")
1426        _("Data version")
1427        """
1428        raise NotImplementedError
1429
1430    def requires_login(self):
1431        """
1432        Returns True for backends that require a login dialog, else False.
1433        """
1434        return False
1435
1436    def method(self, fmt, *args):
1437        """
1438        Convenience function to return database methods.
1439
1440        :param fmt: Method format string.
1441        :type fmt: str
1442        :param args: Substitutions arguments.
1443        :type args: str
1444        :returns: Returns a database method or None.
1445        :rtype: method
1446
1447        Examples::
1448
1449            db.method('get_%s_from_handle, 'Person')
1450            Returns the get_person_from_handle method.
1451
1452            db.method('get_%s_from_%s, 'Event', 'gramps_id')
1453            Returns the get_event_from_gramps_id method.
1454
1455            db.method('get_%s_handles, 'Attribute')
1456            Returns None.  Attribute is not a primary object.
1457
1458        .. warning::  Formats 'iter_%s' and 'get_number_of_%s' are not yet
1459                      implemented.
1460        """
1461        return getattr(self, fmt % tuple([arg.lower() for arg in args]), None)
1462
1463
1464class DbWriteBase(DbReadBase):
1465    """
1466    Gramps database object. This object is a base class for all
1467    database interfaces.  All methods raise NotImplementedError
1468    and must be implemented in the derived class as required.
1469    """
1470
1471    def __init__(self):
1472        """
1473        Create a new DbWriteBase instance.
1474
1475        A new DbWriteBase class should never be directly created. Only classes
1476        derived from this class should be created.
1477        """
1478        DbReadBase.__init__(self)
1479
1480    def add_citation(self, event, transaction, set_gid=True):
1481        """
1482        Add an Citation to the database, assigning internal IDs if they have
1483        not already been defined.
1484
1485        If not set_gid, then gramps_id is not set.
1486        """
1487        raise NotImplementedError
1488
1489    def add_event(self, event, transaction, set_gid=True):
1490        """
1491        Add an Event to the database, assigning internal IDs if they have
1492        not already been defined.
1493
1494        If not set_gid, then gramps_id is not set.
1495        """
1496        raise NotImplementedError
1497
1498    def add_family(self, family, transaction, set_gid=True):
1499        """
1500        Add a Family to the database, assigning internal IDs if they have
1501        not already been defined.
1502
1503        If not set_gid, then gramps_id is not set.
1504        """
1505        raise NotImplementedError
1506
1507    def add_media(self, obj, transaction, set_gid=True):
1508        """
1509        Add a Media to the database, assigning internal IDs if they have
1510        not already been defined.
1511
1512        If not set_gid, then gramps_id is not set.
1513        """
1514        raise NotImplementedError
1515
1516    def add_note(self, obj, transaction, set_gid=True):
1517        """
1518        Add a Note to the database, assigning internal IDs if they have
1519        not already been defined.
1520
1521        If not set_gid, then gramps_id is not set.
1522        """
1523        raise NotImplementedError
1524
1525    def add_person(self, person, transaction, set_gid=True):
1526        """
1527        Add a Person to the database, assigning internal IDs if they have
1528        not already been defined.
1529
1530        If not set_gid, then gramps_id is not set.
1531        """
1532        raise NotImplementedError
1533
1534    def add_place(self, place, transaction, set_gid=True):
1535        """
1536        Add a Place to the database, assigning internal IDs if they have
1537        not already been defined.
1538
1539        If not set_gid, then gramps_id is not set.
1540        """
1541        raise NotImplementedError
1542
1543    def add_repository(self, obj, transaction, set_gid=True):
1544        """
1545        Add a Repository to the database, assigning internal IDs if they have
1546        not already been defined.
1547
1548        If not set_gid, then gramps_id is not set.
1549        """
1550        raise NotImplementedError
1551
1552    def add_source(self, source, transaction, set_gid=True):
1553        """
1554        Add a Source to the database, assigning internal IDs if they have
1555        not already been defined.
1556
1557        If not set_gid, then gramps_id is not set.
1558        """
1559        raise NotImplementedError
1560
1561    def add_tag(self, tag, transaction):
1562        """
1563        Add a Tag to the database, assigning a handle if it has not already
1564        been defined.
1565        """
1566        raise NotImplementedError
1567
1568    def add_to_surname_list(self, person, batch_transaction, name):
1569        """
1570        Add surname from given person to list of surnames
1571        """
1572        raise NotImplementedError
1573
1574    def commit_citation(self, event, transaction, change_time=None):
1575        """
1576        Commit the specified Event to the database, storing the changes as
1577        part of the transaction.
1578        """
1579        raise NotImplementedError
1580
1581    def commit_event(self, event, transaction, change_time=None):
1582        """
1583        Commit the specified Event to the database, storing the changes as
1584        part of the transaction.
1585        """
1586        raise NotImplementedError
1587
1588    def commit_family(self, family, transaction, change_time=None):
1589        """
1590        Commit the specified Family to the database, storing the changes as
1591        part of the transaction.
1592        """
1593        raise NotImplementedError
1594
1595    def commit_media(self, obj, transaction, change_time=None):
1596        """
1597        Commit the specified Media to the database, storing the changes
1598        as part of the transaction.
1599        """
1600        raise NotImplementedError
1601
1602    def commit_note(self, note, transaction, change_time=None):
1603        """
1604        Commit the specified Note to the database, storing the changes as part
1605        of the transaction.
1606        """
1607        raise NotImplementedError
1608
1609    def commit_person(self, person, transaction, change_time=None):
1610        """
1611        Commit the specified Person to the database, storing the changes as
1612        part of the transaction.
1613        """
1614        raise NotImplementedError
1615
1616    def commit_place(self, place, transaction, change_time=None):
1617        """
1618        Commit the specified Place to the database, storing the changes as
1619        part of the transaction.
1620        """
1621        raise NotImplementedError
1622
1623    def commit_repository(self, repository, transaction, change_time=None):
1624        """
1625        Commit the specified Repository to the database, storing the changes
1626        as part of the transaction.
1627        """
1628        raise NotImplementedError
1629
1630    def commit_source(self, source, transaction, change_time=None):
1631        """
1632        Commit the specified Source to the database, storing the changes as
1633        part of the transaction.
1634        """
1635        raise NotImplementedError
1636
1637    def commit_tag(self, tag, transaction, change_time=None):
1638        """
1639        Commit the specified Tag to the database, storing the changes as
1640        part of the transaction.
1641        """
1642        raise NotImplementedError
1643
1644    def get_undodb(self):
1645        """
1646        Return the database that keeps track of Undo/Redo operations.
1647        """
1648        raise NotImplementedError
1649
1650    def rebuild_secondary(self, callback):
1651        """
1652        Rebuild secondary indices
1653        """
1654        raise NotImplementedError
1655
1656    def reindex_reference_map(self, callback):
1657        """
1658        Reindex all primary records in the database.
1659        """
1660        raise NotImplementedError
1661
1662    def remove_citation(self, handle, transaction):
1663        """
1664        Remove the Event specified by the database handle from the
1665        database, preserving the change in the passed transaction.
1666        """
1667        raise NotImplementedError
1668
1669    def remove_event(self, handle, transaction):
1670        """
1671        Remove the Event specified by the database handle from the
1672        database, preserving the change in the passed transaction.
1673        """
1674        raise NotImplementedError
1675
1676    def remove_family(self, handle, transaction):
1677        """
1678        Remove the Family specified by the database handle from the
1679        database, preserving the change in the passed transaction.
1680        """
1681        raise NotImplementedError
1682
1683    def remove_media(self, handle, transaction):
1684        """
1685        Remove the MediaPerson specified by the database handle from the
1686        database, preserving the change in the passed transaction.
1687        """
1688        raise NotImplementedError
1689
1690    def remove_note(self, handle, transaction):
1691        """
1692        Remove the Note specified by the database handle from the
1693        database, preserving the change in the passed transaction.
1694        """
1695        raise NotImplementedError
1696
1697    def remove_person(self, handle, transaction):
1698        """
1699        Remove the Person specified by the database handle from the database,
1700        preserving the change in the passed transaction.
1701        """
1702        raise NotImplementedError
1703
1704    def remove_place(self, handle, transaction):
1705        """
1706        Remove the Place specified by the database handle from the
1707        database, preserving the change in the passed transaction.
1708        """
1709        raise NotImplementedError
1710
1711    def remove_repository(self, handle, transaction):
1712        """
1713        Remove the Repository specified by the database handle from the
1714        database, preserving the change in the passed transaction.
1715        """
1716        raise NotImplementedError
1717
1718    def remove_source(self, handle, transaction):
1719        """
1720        Remove the Source specified by the database handle from the
1721        database, preserving the change in the passed transaction.
1722        """
1723        raise NotImplementedError
1724
1725    def remove_tag(self, handle, transaction):
1726        """
1727        Remove the Tag specified by the database handle from the
1728        database, preserving the change in the passed transaction.
1729        """
1730        raise NotImplementedError
1731
1732    def remove_from_surname_list(self, person):
1733        """
1734        Check whether there are persons with the same surname left in
1735        the database.
1736
1737        If not then we need to remove the name from the list.
1738        The function must be overridden in the derived class.
1739        """
1740        raise NotImplementedError
1741
1742    def set_default_person_handle(self, handle):
1743        """
1744        Set the default Person to the passed instance.
1745        """
1746        raise NotImplementedError
1747
1748    def set_name_group_mapping(self, name, group):
1749        """
1750        Set the default grouping name for a surname.
1751
1752        Needs to be overridden in the derived class.
1753        """
1754        raise NotImplementedError
1755
1756    def transaction_begin(self, transaction):
1757        """
1758        Prepare the database for the start of a new transaction.
1759
1760        Two modes should be provided: transaction.batch=False for ordinary
1761        database operations that will be encapsulated in database transactions
1762        to make them ACID and that are added to Gramps transactions so that
1763        they can be undone. And transaction.batch=True for lengthy database
1764        operations, that benefit from a speedup by making them none ACID, and
1765        that can't be undone. The user is warned and is asked for permission
1766        before the start of such database operations.
1767
1768        :param transaction: Gramps transaction ...
1769        :type transaction: :py:class:`.DbTxn`
1770        :returns: Returns the Gramps transaction.
1771        :rtype: :py:class:`.DbTxn`
1772        """
1773        raise NotImplementedError
1774
1775    def transaction_commit(self, transaction):
1776        """
1777        Make the changes to the database final and add the content of the
1778        transaction to the undo database.
1779        """
1780        raise NotImplementedError
1781
1782    def transaction_abort(self, transaction):
1783        """
1784        Revert the changes made to the database so far during the transaction.
1785        """
1786        raise NotImplementedError
1787
1788    def undo(self, update_history=True):
1789        """
1790        Undo last transaction.
1791        """
1792        raise NotImplementedError
1793
1794    def redo(self, update_history=True):
1795        """
1796        Redo last transaction.
1797        """
1798        raise NotImplementedError
1799
1800    def add_child_to_family(self, family, child,
1801                            mrel=ChildRefType(),
1802                            frel=ChildRefType(),
1803                            trans=None):
1804        """
1805        Adds a child to a family.
1806        """
1807        cref = ChildRef()
1808        cref.ref = child.handle
1809        cref.set_father_relation(frel)
1810        cref.set_mother_relation(mrel)
1811
1812        family.add_child_ref(cref)
1813        child.add_parent_family_handle(family.handle)
1814
1815        if trans is None:
1816            with DbTxn(_('Add child to family'), self) as trans:
1817                self.commit_family(family, trans)
1818                self.commit_person(child, trans)
1819        else:
1820            self.commit_family(family, trans)
1821            self.commit_person(child, trans)
1822
1823    def remove_child_from_family(self, person_handle, family_handle,
1824                                 trans=None):
1825        """
1826        Remove a person as a child of the family, deleting the family if
1827        it becomes empty.
1828        """
1829        if trans is None:
1830            with DbTxn(_("Remove child from family"), self) as trans:
1831                self.__remove_child_from_family(person_handle, family_handle,
1832                                                trans)
1833        else:
1834            self.__remove_child_from_family(person_handle, family_handle,
1835                                            trans)
1836            trans.set_description(_("Remove child from family"))
1837
1838    def __remove_child_from_family(self, person_handle, family_handle, trans):
1839        """
1840        Remove a person as a child of the family, deleting the family if
1841        it becomes empty; trans is compulsory.
1842        """
1843        person = self.get_person_from_handle(person_handle)
1844        family = self.get_family_from_handle(family_handle)
1845        person.remove_parent_family_handle(family_handle)
1846        family.remove_child_handle(person_handle)
1847
1848        if (not family.get_father_handle() and not family.get_mother_handle()
1849                and not family.get_child_ref_list()):
1850            self.remove_family_relationships(family_handle, trans)
1851        else:
1852            self.commit_family(family, trans)
1853        self.commit_person(person, trans)
1854
1855    def delete_person_from_database(self, person, trans):
1856        """
1857        Deletes a person from the database, cleaning up all associated
1858        references.
1859        """
1860
1861        # clear out the default person if the person is the default person
1862        if self.get_default_person() == person:
1863            self.set_default_person_handle(None)
1864
1865        # loop through the family list
1866        for family_handle in person.get_family_handle_list():
1867            if not family_handle:
1868                continue
1869
1870            family = self.get_family_from_handle(family_handle)
1871
1872            if person.get_handle() == family.get_father_handle():
1873                family.set_father_handle(None)
1874            else:
1875                family.set_mother_handle(None)
1876
1877            if not family.get_father_handle() and \
1878                    not family.get_mother_handle() and \
1879                    not family.get_child_ref_list():
1880                self.remove_family_relationships(family_handle, trans)
1881            else:
1882                self.commit_family(family, trans)
1883
1884        for family_handle in person.get_parent_family_handle_list():
1885            if family_handle:
1886                family = self.get_family_from_handle(family_handle)
1887                family.remove_child_handle(person.get_handle())
1888                if not family.get_father_handle() and \
1889                        not family.get_mother_handle() and \
1890                        not family.get_child_ref_list():
1891                    self.remove_family_relationships(family_handle, trans)
1892                else:
1893                    self.commit_family(family, trans)
1894
1895        handle = person.get_handle()
1896
1897        person_list = [
1898            item[1] for item in
1899            self.find_backlink_handles(handle, ['Person'])]
1900
1901        for phandle in person_list:
1902            prsn = self.get_person_from_handle(phandle)
1903            prsn.remove_handle_references('Person', [handle])
1904            self.commit_person(prsn, trans)
1905        self.remove_person(handle, trans)
1906
1907    def remove_family_relationships(self, family_handle, trans=None):
1908        """
1909        Remove a family and its relationships.
1910        """
1911        if trans is None:
1912            with DbTxn(_("Remove Family"), self) as trans:
1913                self.__remove_family_relationships(family_handle, trans)
1914        else:
1915            self.__remove_family_relationships(family_handle, trans)
1916            trans.set_description(_("Remove Family"))
1917
1918    def __remove_family_relationships(self, family_handle, trans):
1919        """
1920        Remove a family and all that references it; trans is compulsory.
1921        """
1922        person_list = [item[1] for item in
1923                self.find_backlink_handles(family_handle, ['Person'])]
1924        for phandle in person_list:
1925            person = self.get_person_from_handle(phandle)
1926            if person:
1927                person.remove_handle_references('Family', [family_handle])
1928                self.commit_person(person, trans)
1929        self.remove_family(family_handle, trans)
1930
1931    def remove_parent_from_family(self, person_handle, family_handle,
1932                                  trans=None):
1933        """
1934        Remove a person as either the father or mother of a family,
1935        deleting the family if it becomes empty.
1936        """
1937        if trans is None:
1938            with DbTxn('', self) as trans:
1939                msg = self.__remove_parent_from_family(person_handle,
1940                                                       family_handle, trans)
1941                trans.set_description(msg)
1942        else:
1943            msg = self.__remove_parent_from_family(person_handle,
1944                                                   family_handle, trans)
1945            trans.set_description(msg)
1946
1947    def __remove_parent_from_family(self, person_handle, family_handle, trans):
1948        """
1949        Remove a person as either the father or mother of a family,
1950        deleting the family if it becomes empty; trans is compulsory.
1951        """
1952        person = self.get_person_from_handle(person_handle)
1953        family = self.get_family_from_handle(family_handle)
1954
1955        person.remove_family_handle(family_handle)
1956        if family.get_father_handle() == person_handle:
1957            family.set_father_handle(None)
1958            msg = _("Remove father from family")
1959        elif family.get_mother_handle() == person_handle:
1960            msg = _("Remove mother from family")
1961            family.set_mother_handle(None)
1962        else:
1963            raise DbTransactionCancel("The relation between the person and "
1964                "the family you try to remove is not consistent, please fix "
1965                "that first, for example from the family editor or by running "
1966                "the database repair tool, before removing the family.")
1967
1968        if (not family.get_father_handle() and not family.get_mother_handle()
1969                and not family.get_child_ref_list()):
1970            self.remove_family_relationships(family_handle, trans)
1971        else:
1972            self.commit_family(family, trans)
1973        self.commit_person(person, trans)
1974        return msg
1975
1976    def marriage_from_eventref_list(self, eventref_list):
1977        """
1978        Get the marriage event from an eventref list.
1979        """
1980        for eventref in eventref_list:
1981            event = self.get_event_from_handle(eventref.ref)
1982            if event and event.type.is_marriage():
1983                return event
1984        return None
1985
1986    def get_total(self):
1987        """
1988        Get the total of primary objects.
1989        """
1990        person_len = self.get_number_of_people()
1991        family_len = self.get_number_of_families()
1992        event_len = self.get_number_of_events()
1993        place_len = self.get_number_of_places()
1994        repo_len = self.get_number_of_repositories()
1995        source_len = self.get_number_of_sources()
1996        citation_len = self.get_number_of_citations()
1997        media_len = self.get_number_of_media()
1998        note_len = self.get_number_of_notes()
1999        tag_len = self.get_number_of_tags()
2000
2001        return (person_len + family_len + event_len + place_len + repo_len +
2002                source_len + citation_len + media_len + note_len + tag_len)
2003
2004    def set_birth_death_index(self, person):
2005        """
2006        Set the birth and death indices for a person.
2007        """
2008        birth_ref_index = -1
2009        death_ref_index = -1
2010        event_ref_list = person.get_event_ref_list()
2011        for index in range(len(event_ref_list)):
2012            ref = event_ref_list[index]
2013            event = self.get_event_from_handle(ref.ref)
2014            if (event.type.is_birth()
2015                and ref.role.is_primary()
2016                and (birth_ref_index == -1)):
2017                birth_ref_index = index
2018            elif (event.type.is_death()
2019                  and ref.role.is_primary()
2020                  and (death_ref_index == -1)):
2021                death_ref_index = index
2022
2023        person.birth_ref_index = birth_ref_index
2024        person.death_ref_index = death_ref_index
2025