1   
   2   
   3   
   4   
   5   
   6   
   7   
   8   
   9   
  10   
  11   
  12   
  13   
  14   
  15   
  16   
  17   
  18   
  19   
  20   
  21   
  22   
  23  """ 
  24  Base class for the GRAMPS databases. All database interfaces should inherit 
  25  from this class. 
  26  """ 
  27   
  28   
  29   
  30   
  31   
  32   
  33  import cPickle 
  34  import time 
  35  import random 
  36  import locale 
  37  import os 
  38  from sys import maxint 
  39  from bsddb import db 
  40  from gettext import gettext as _ 
  41   
  42  import logging 
  43  LOG = logging.getLogger(".GrampsDb") 
  44   
  45   
  46   
  47   
  48   
  49   
  50  from gen.lib import (MediaObject, Person, Family, Source, Event, Place,  
  51                       Repository, Note, GenderStats, Researcher) 
  52  from gen.utils.callback import Callback 
  53  from gen.db.iterator import CursorIterator 
  54   
  55   
  56   
  57   
  58   
  59   
  60  from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY,  
  61                              MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, NOTE_KEY,  
  62                              REFERENCE_KEY, PERSON_COL_KEY, FAMILY_COL_KEY,  
  63                              CHILD_COL_KEY, PLACE_COL_KEY, SOURCE_COL_KEY,  
  64                              MEDIA_COL_KEY, EVENT_COL_KEY, REPOSITORY_COL_KEY,  
  65                              NOTE_COL_KEY) 
  66   
  67  _UNDO_SIZE = 1000 
  68   
  69   
  70   
  71   
  72   
  73   
  74   
  75   
  76  CLASS_TO_KEY_MAP = {Person.__name__: PERSON_KEY,  
  77                      Family.__name__: FAMILY_KEY,  
  78                      Source.__name__: SOURCE_KEY,  
  79                      Event.__name__: EVENT_KEY,  
  80                      MediaObject.__name__: MEDIA_KEY,  
  81                      Place.__name__: PLACE_KEY,  
  82                      Repository.__name__:REPOSITORY_KEY, 
  83                      Note.__name__: NOTE_KEY} 
  84   
  85  KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__,  
  86                      FAMILY_KEY: Family.__name__,  
  87                      SOURCE_KEY: Source.__name__,  
  88                      EVENT_KEY: Event.__name__,  
  89                      MEDIA_KEY: MediaObject.__name__,  
  90                      PLACE_KEY: Place.__name__,  
  91                      REPOSITORY_KEY: Repository.__name__, 
  92                      NOTE_KEY: Note.__name__} 
  93   
  94  _SIGBASE = ('person', 'family', 'source', 'event',  
  95              'media', 'place', 'repository', 'reference', 'note') 
  96   
  99          self.bookmarks = list(default)  
  100   
 101 -    def set(self, new_list): 
  102          self.bookmarks = list(new_list) 
  103   
 105          return self.bookmarks 
  106   
 108          self.bookmarks.append(item) 
  109   
 111          self.bookmarks += blist 
  112   
 114          self.bookmarks.remove(item) 
  115   
 116 -    def pop(self, item): 
  117          return self.bookmarks.pop(item) 
  118   
 120          self.bookmarks.insert(pos, item) 
  123      """ 
 124      GRAMPS database object. This object is a base class for all 
 125      database interfaces. 
 126      """ 
 127   
 128       
 129       
 130      __config__ = None 
 131       
 132      __signals__ = { 
 133          'person-add'         : (list, ),  
 134          'person-update'      : (list, ),  
 135          'person-delete'      : (list, ),  
 136          'person-rebuild'     : None,  
 137          'family-add'         : (list, ),  
 138          'family-update'      : (list, ),  
 139          'family-delete'      : (list, ),  
 140          'family-rebuild'     : None,  
 141          'source-add'         : (list, ),  
 142          'source-update'      : (list, ),  
 143          'source-delete'      : (list, ),  
 144          'source-rebuild'     : None,  
 145          'place-add'          : (list, ),  
 146          'place-update'       : (list, ),  
 147          'place-delete'       : (list, ),  
 148          'place-rebuild'      : None,  
 149          'media-add'          : (list, ),  
 150          'media-update'       : (list, ),  
 151          'media-delete'       : (list, ),  
 152          'media-rebuild'      : None,  
 153          'event-add'          : (list, ),  
 154          'event-update'       : (list, ),  
 155          'event-delete'       : (list, ),  
 156          'event-rebuild'      : None,  
 157          'repository-add'     : (list, ),  
 158          'repository-update'  : (list, ),  
 159          'repository-delete'  : (list, ),  
 160          'repository-rebuild' : None,  
 161          'note-add'           : (list, ),  
 162          'note-update'        : (list, ),  
 163          'note-delete'        : (list, ),  
 164          'note-rebuild'       : None,  
 165          'long-op-start'      : (object, ), 
 166          'long-op-heartbeat'  : None, 
 167          'long-op-end'        : None 
 168          } 
 169       
 170       
 171      try: 
 172          _LOG_ALL = int(os.environ.get('GRAMPS_SIGNAL', "0")) == 1 
 173      except: 
 174          _LOG_ALL = False 
 175   
 176   
 178          """ 
 179          Create a new GrampsDbBase instance.  
 180           
 181          A new GrampDbBase class should never be directly created. Only classes  
 182          derived from this class should be created. 
 183          """ 
 184   
 185          Callback.__init__(self) 
 186   
 187          self.set_person_id_prefix('I%04d') 
 188          self.set_object_id_prefix('O%04d') 
 189          self.set_family_id_prefix('F%04d') 
 190          self.set_source_id_prefix('S%04d') 
 191          self.set_place_id_prefix('P%04d') 
 192          self.set_event_id_prefix('E%04d') 
 193          self.set_repository_id_prefix('R%04d') 
 194          self.set_note_id_prefix('N%04d') 
 195   
 196          self.readonly = False 
 197          self.rand = random.Random(time.time()) 
 198          self.smap_index = 0 
 199          self.emap_index = 0 
 200          self.pmap_index = 0 
 201          self.fmap_index = 0 
 202          self.lmap_index = 0 
 203          self.omap_index = 0 
 204          self.rmap_index = 0 
 205          self.nmap_index = 0 
 206          self.db_is_open = False 
 207   
 208          self.family_event_names = set() 
 209          self.individual_event_names = set() 
 210          self.individual_attributes = set() 
 211          self.family_attributes = set() 
 212          self.marker_names = set() 
 213          self.child_ref_types = set() 
 214          self.family_rel_types = set() 
 215          self.event_role_names = set() 
 216          self.name_types = set() 
 217          self.repository_types = set() 
 218          self.note_types = set() 
 219          self.source_media_types = set() 
 220          self.url_types = set() 
 221          self.media_attributes = set() 
 222   
 223          self.open = 0 
 224          self.genderStats = GenderStats() 
 225   
 226          self.undodb    = [] 
 227          self.id_trans  = {} 
 228          self.fid_trans = {} 
 229          self.pid_trans = {} 
 230          self.sid_trans = {} 
 231          self.oid_trans = {} 
 232          self.rid_trans = {} 
 233          self.nid_trans = {} 
 234          self.eid_trans = {} 
 235          self.env = None 
 236          self.person_map = {} 
 237          self.family_map = {} 
 238          self.place_map  = {} 
 239          self.source_map = {} 
 240          self.repository_map  = {} 
 241          self.note_map = {} 
 242          self.media_map  = {} 
 243          self.event_map  = {} 
 244          self.metadata   = {} 
 245          self.name_group = {} 
 246          self.undo_callback = None 
 247          self.redo_callback = None 
 248          self.undo_history_callback = None 
 249          self.modified   = 0 
 250   
 251          self.undoindex  = -1 
 252          self.translist  = [None] * _UNDO_SIZE 
 253          self.abort_possible = True 
 254          self.undo_history_timestamp = 0 
 255          self.default = None 
 256          self.owner = Researcher() 
 257          self.name_formats = [] 
 258          self.bookmarks = GrampsDbBookmarks() 
 259          self.family_bookmarks = GrampsDbBookmarks() 
 260          self.event_bookmarks = GrampsDbBookmarks() 
 261          self.place_bookmarks = GrampsDbBookmarks() 
 262          self.source_bookmarks = GrampsDbBookmarks() 
 263          self.repo_bookmarks = GrampsDbBookmarks() 
 264          self.media_bookmarks = GrampsDbBookmarks() 
 265          self.note_bookmarks = GrampsDbBookmarks() 
 266          self._bm_changes = 0 
 267          self.path = "" 
 268          self.surname_list = [] 
  269   
 270 -    def set_prefixes(self, person, media, family, source, place, event, 
 271                       repository, note): 
  272          self.person_prefix = person 
 273          self.mediaobject_prefix = media 
 274          self.family_prefix = family 
 275          self.source_prefix = source 
 276          self.place_prefix = place 
 277          self.event_prefix = event 
 278          self.repository_prefix = repository 
 279          self.note_prefix = note 
  280   
 283   
 285          """Return True when the file has a supported version.""" 
 286          return True 
  287   
 290   
 293   
 296   
 299   
 302   
 305   
 308   
 311   
 314   
 317   
 319          return "%08x%08x" % ( int(time.time()*10000),  
 320                                self.rand.randint(0, maxint)) 
  321   
 323          raise NotImplementedError 
  324   
 327   
 329          raise NotImplementedError 
  330   
 333   
 335          raise NotImplementedError 
  336   
 339   
 341          raise NotImplementedError 
  342   
 345   
 347          raise NotImplementedError 
  348   
 351   
 354   
 357   
 359          raise NotImplementedError 
  360   
 363   
 365          raise NotImplementedError 
  366   
 369   
 371          if not self.readonly: 
 372              self.undolog = "%s.undo" % self.full_name 
 373              self.undodb = db.DB() 
 374              self.undodb.open(self.undolog, db.DB_RECNO, db.DB_CREATE) 
  375   
 377          if not self.readonly: 
 378              self.undodb.close() 
 379              try: 
 380                  os.remove(self.undolog) 
 381              except: 
 382                  pass 
  383   
 384 -    def load(self, name, callback, mode="w"): 
  385          """ 
 386          Open the specified database.  
 387           
 388          The method needs to be overridden in the derived class. 
 389          """ 
 390          raise NotImplementedError 
  391   
 392 -    def load_from(self, other_database, filename, callback): 
  393          """ 
 394          Load data from the other database into itself. 
 395           
 396          The filename is the name of the file for the newly created database. 
 397          The method needs to be overridden in the derived class. 
 398          """ 
 399          raise NotImplementedError 
  400   
 402          """ 
 403          Close the specified database.  
 404           
 405          The method needs to be overridden in the derived class. 
 406          """ 
 407          pass 
  408           
 410          """ 
 411          Return 1 if the database has been opened. 
 412          """ 
 413          return self.db_is_open 
  414   
 416          """ 
 417          Notify clients that the data has changed significantly, and that all 
 418          internal data dependent on the database should be rebuilt. 
 419          """ 
 420          self.emit('person-rebuild') 
 421          self.emit('family-rebuild') 
 422          self.emit('place-rebuild') 
 423          self.emit('source-rebuild') 
 424          self.emit('media-rebuild') 
 425          self.emit('event-rebuild') 
 426          self.emit('repository-rebuild') 
 427          self.emit('note-rebuild') 
  428               
 429 -    def commit_base(self, obj, data_map, key, update_list, add_list,  
 430                       transaction, change_time): 
  431          """ 
 432          Commit the specified Person to the database, storing the changes as  
 433          part of the transaction. 
 434          """ 
 435          if self.readonly or not obj or not obj.handle: 
 436              return  
 437   
 438          if change_time: 
 439              obj.change = int(change_time) 
 440          else: 
 441              obj.change = int(time.time()) 
 442          handle = str(obj.handle) 
 443           
 444          self.update_reference_map(obj, transaction) 
 445   
 446          if transaction.batch: 
 447              data_map[handle] = obj.serialize() 
 448              old_data = None 
 449          else: 
 450              old_data = data_map.get(handle) 
 451              new_data = obj.serialize() 
 452              transaction.add(key, handle, old_data, new_data) 
 453              if old_data: 
 454                  update_list.append((handle, new_data)) 
 455              else: 
 456                  add_list.append((handle, new_data)) 
 457          return old_data 
  458   
 505   
 518               
 520          """ 
 521          Commit the specified Source to the database, storing the changes as  
 522          part of the transaction. 
 523          """ 
 524   
 525          self.commit_base(source, self.source_map, SOURCE_KEY,  
 526                            transaction.source_update, transaction.source_add,  
 527                            transaction, change_time) 
 528   
 529          self.source_media_types.update( 
 530              [str(ref.media_type) for ref in source.reporef_list 
 531               if ref.media_type.is_custom()])        
 532   
 533          attr_list = [] 
 534          for mref in source.media_list: 
 535              attr_list += [str(attr.type) for attr in mref.attribute_list 
 536                            if attr.type.is_custom() and str(attr.type)] 
 537          self.media_attributes.update(attr_list) 
  538   
 539 -    def commit_place(self, place, transaction, change_time=None): 
  540          """ 
 541          Commit the specified Place to the database, storing the changes as  
 542          part of the transaction. 
 543          """ 
 544   
 545          self.commit_base(place, self.place_map, PLACE_KEY,  
 546                            transaction.place_update, transaction.place_add,  
 547                            transaction, change_time) 
 548   
 549          self.url_types.update([str(url.type) for url in place.urls 
 550                                 if url.type.is_custom()]) 
 551   
 552          attr_list = [] 
 553          for mref in place.media_list: 
 554              attr_list += [str(attr.type) for attr in mref.attribute_list 
 555                            if attr.type.is_custom() and str(attr.type)] 
 556          self.media_attributes.update(attr_list) 
  557   
 562   
 567   
 568 -    def commit_event(self, event, transaction, change_time=None): 
  569          """ 
 570          Commit the specified Event to the database, storing the changes as  
 571          part of the transaction. 
 572          """ 
 573          self.commit_base(event, self.event_map, EVENT_KEY,  
 574                            transaction.event_update, transaction.event_add,  
 575                            transaction, change_time) 
 576   
 577          attr_list = [] 
 578          for mref in event.media_list: 
 579              attr_list += [str(attr.type) for attr in mref.attribute_list 
 580                            if attr.type.is_custom() and str(attr.type)] 
 581          self.media_attributes.update(attr_list) 
  582   
 584          """ 
 585          Commit the specified Family to the database, storing the changes as  
 586          part of the transaction. 
 587          """ 
 588   
 589          self.commit_base(family, self.family_map, FAMILY_KEY,  
 590                            transaction.family_update, transaction.family_add,  
 591                            transaction, change_time) 
 592   
 593          self.family_attributes.update( 
 594              [str(attr.type) for attr in family.attribute_list 
 595               if attr.type.is_custom() and str(attr.type)]) 
 596   
 597          rel_list = [] 
 598          for ref in family.child_ref_list: 
 599              if ref.frel.is_custom(): 
 600                  rel_list.append(str(ref.frel)) 
 601              if ref.mrel.is_custom(): 
 602                  rel_list.append(str(ref.mrel)) 
 603          self.child_ref_types.update(rel_list) 
 604   
 605          self.event_role_names.update( 
 606              [str(eref.role) for eref in family.event_ref_list 
 607               if eref.role.is_custom()]) 
 608   
 609          if family.type.is_custom(): 
 610              self.family_rel_types.add(str(family.type)) 
 611   
 612          attr_list = [] 
 613          for mref in family.media_list: 
 614              attr_list += [str(attr.type) for attr in mref.attribute_list 
 615                            if attr.type.is_custom() and str(attr.type)] 
 616          self.media_attributes.update(attr_list) 
  617   
 619          """ 
 620          Commit the specified Repository to the database, storing the changes 
 621          as part of the transaction. 
 622          """ 
 623          self.commit_base(repository, self.repository_map, REPOSITORY_KEY,  
 624                            transaction.repository_update, 
 625                            transaction.repository_add,  
 626                            transaction, change_time) 
 627   
 628          if repository.type.is_custom(): 
 629              self.repository_types.add(str(repository.type)) 
 630   
 631          self.url_types.update([str(url.type) for url in repository.urls 
 632                                 if url.type.is_custom()]) 
  633   
 634 -    def commit_note(self, note, transaction, change_time=None): 
  635          """ 
 636          Commit the specified Note to the database, storing the changes as part  
 637          of the transaction. 
 638          """ 
 639          self.commit_base(note, self.note_map, NOTE_KEY,  
 640                            transaction.note_update, 
 641                            transaction.note_add,  
 642                            transaction, change_time) 
 643   
 644          if note.type.is_custom(): 
 645              self.note_types.add(str(note.type)) 
  646   
 648          """ 
 649          Return the next available GRAMPS' ID for a Person object based off the  
 650          person ID prefix. 
 651          """ 
 652          index = self.person_prefix % self.pmap_index 
 653          while self.id_trans.has_key(str(index)): 
 654              self.pmap_index += 1 
 655              index = self.person_prefix % self.pmap_index 
 656          self.pmap_index += 1 
 657          return index 
  658   
 660          """ 
 661          Return the next available GRAMPS' ID for a Place object based off the  
 662          place ID prefix. 
 663          """ 
 664          index = self.place_prefix % self.lmap_index 
 665          while self.pid_trans.has_key(str(index)): 
 666              self.lmap_index += 1 
 667              index = self.place_prefix % self.lmap_index 
 668          self.lmap_index += 1 
 669          return index 
  670   
 672          """ 
 673          Return the next available GRAMPS' ID for a Event object based off the  
 674          event ID prefix. 
 675          """ 
 676          index = self.event_prefix % self.emap_index 
 677          while self.eid_trans.has_key(str(index)): 
 678              self.emap_index += 1 
 679              index = self.event_prefix % self.emap_index 
 680          self.emap_index += 1 
 681          return index 
  682   
 684          """ 
 685          Return the next available GRAMPS' ID for a MediaObject object based 
 686          off the media object ID prefix. 
 687          """ 
 688          index = self.mediaobject_prefix % self.omap_index 
 689          while self.oid_trans.has_key(str(index)): 
 690              self.omap_index += 1 
 691              index = self.mediaobject_prefix % self.omap_index 
 692          self.omap_index += 1 
 693          return index 
  694   
 696          """ 
 697          Return the next available GRAMPS' ID for a Source object based off the  
 698          source ID prefix. 
 699          """ 
 700          index = self.source_prefix % self.smap_index 
 701          while self.sid_trans.has_key(str(index)): 
 702              self.smap_index += 1 
 703              index = self.source_prefix % self.smap_index 
 704          self.smap_index += 1 
 705          return index 
  706   
 708          """ 
 709          Return the next available GRAMPS' ID for a Family object based off the  
 710          family ID prefix. 
 711          """ 
 712          index = self.family_prefix % self.fmap_index 
 713          while self.fid_trans.has_key(str(index)): 
 714              self.fmap_index += 1 
 715              index = self.family_prefix % self.fmap_index 
 716          self.fmap_index += 1 
 717          return index 
  718   
 720          """ 
 721          Return the next available GRAMPS' ID for a Respository object based  
 722          off the repository ID prefix. 
 723          """ 
 724          index = self.repository_prefix % self.rmap_index 
 725          while self.rid_trans.has_key(str(index)): 
 726              self.rmap_index += 1 
 727              index = self.repository_prefix % self.rmap_index 
 728          self.rmap_index += 1 
 729          return index 
  730   
 732          """ 
 733          Return the next available GRAMPS' ID for a Note object based off the  
 734          note ID prefix. 
 735          """ 
 736          index = self.note_prefix % self.nmap_index 
 737          while self.nid_trans.has_key(str(index)): 
 738              self.nmap_index += 1 
 739              index = self.note_prefix % self.nmap_index 
 740          self.nmap_index += 1 
 741          return index 
  742   
 744          data = data_map.get(str(handle)) 
 745          if data: 
 746              newobj = class_type() 
 747              newobj.unserialize(data) 
 748              return newobj 
 749          return None 
  750   
 752          """ 
 753          Find a Person in the database from the passed gramps' ID. 
 754           
 755          If no such Person exists, None is returned. 
 756          """ 
 757          return self.get_from_handle(handle, Person, self.person_map) 
  758   
 760          """ 
 761          Find a Source in the database from the passed gramps' ID. 
 762           
 763          If no such Source exists, None is returned. 
 764          """ 
 765          return self.get_from_handle(handle, Source, self.source_map) 
  766   
 768          """ 
 769          Find an Object in the database from the passed gramps' ID. 
 770           
 771          If no such Object exists, None is returned. 
 772          """ 
 773          return self.get_from_handle(handle, MediaObject, self.media_map) 
  774   
 776          """ 
 777          Find a Place in the database from the passed gramps' ID. 
 778           
 779          If no such Place exists, None is returned. 
 780          """ 
 781          return self.get_from_handle(handle, Place, self.place_map) 
  782   
 784          """ 
 785          Find a Event in the database from the passed gramps' ID. 
 786           
 787          If no such Event exists, None is returned. 
 788          """ 
 789          return self.get_from_handle(handle, Event, self.event_map) 
  790   
 792          """ 
 793          Find a Family in the database from the passed gramps' ID. 
 794           
 795          If no such Family exists, None is returned. 
 796          """ 
 797          return self.get_from_handle(handle, Family, self.family_map) 
  798   
 800          """ 
 801          Find a Repository in the database from the passed gramps' ID. 
 802           
 803          If no such Repository exists, None is returned. 
 804          """ 
 805          return self.get_from_handle(handle, Repository, self.repository_map) 
  806   
 808          """ 
 809          Find a Note in the database from the passed gramps' ID. 
 810           
 811          If no such Note exists, None is returned. 
 812          """ 
 813          return self.get_from_handle(handle, Note, self.note_map) 
  814   
 815 -    def find_from_handle(self, handle, transaction, class_type, dmap, 
 816                            add_func): 
  817          """ 
 818          Find a object of class_type in the database from the passed handle. 
 819           
 820          If no object exists, a new object is added to the database. 
 821           
 822          @return: Returns a tuple, first the object, second a bool which is True 
 823                   if the object is new 
 824          @rtype: tuple 
 825          """ 
 826          obj = class_type() 
 827          handle = str(handle) 
 828          new = True 
 829          if dmap.has_key(handle): 
 830              obj.unserialize(dmap.get(handle)) 
 831               
 832              if obj.gramps_id is not None: 
 833                  new = False 
 834          else: 
 835              obj.set_handle(handle) 
 836              add_func(obj, transaction) 
 837          return obj, new 
  838   
 839 -    def __check_from_handle(self, handle, transaction, class_type, dmap, 
 840                              add_func, set_gid=True): 
  841          handle = str(handle) 
 842          if not dmap.has_key(handle): 
 843              obj = class_type() 
 844              obj.set_handle(handle) 
 845              add_func(obj, transaction, set_gid=set_gid) 
  846   
 848          """ 
 849          Find a Person in the database from the passed handle. 
 850           
 851          If no such Person exists, a new Person is added to the database. 
 852           
 853          @return: Returns a tuple, first the object, second a bool which is True 
 854                   if the object is new 
 855          @rtype: tuple 
 856          """ 
 857          return self.find_from_handle(handle, transaction, Person,  
 858                                       self.person_map, self.add_person) 
  859   
 861          """ 
 862          Find a Source in the database from the passed handle. 
 863           
 864          If no such Source exists, a new Source is added to the database. 
 865           
 866          @return: Returns a tuple, first the object, second a bool which is True 
 867                   if the object is new 
 868          @rtype: tuple 
 869          """ 
 870          return self.find_from_handle(handle, transaction, Source,  
 871                                       self.source_map, self.add_source) 
  872   
 874          """ 
 875          Find a Event in the database from the passed handle. 
 876           
 877          If no such Event exists, a new Event is added to the database. 
 878           
 879          @return: Returns a tuple, first the object, second a bool which is True 
 880                   if the object is new 
 881          @rtype: tuple 
 882          """ 
 883          return self.find_from_handle(handle, transaction, Event,  
 884                                       self.event_map, self.add_event) 
  885   
 887          """ 
 888          Find a MediaObject in the database from the passed handle. 
 889           
 890          If no such MediaObject exists, a new Object is added to the database. 
 891           
 892          @return: Returns a tuple, first the object, second a bool which is True 
 893                   if the object is new 
 894          @rtype: tuple 
 895          """ 
 896          return self.find_from_handle(handle, transaction, MediaObject,  
 897                                       self.media_map, self.add_object) 
  898   
 900          """ 
 901          Find a Place in the database from the passed handle. 
 902           
 903          If no such Place exists, a new Place is added to the database. 
 904           
 905          @return: Returns a tuple, first the object, second a bool which is True 
 906                   if the object is new 
 907          @rtype: tuple 
 908          """ 
 909          return self.find_from_handle(handle, transaction, Place,  
 910                                       self.place_map, self.add_place) 
  911   
 913          """ 
 914          Find a Family in the database from the passed handle. 
 915           
 916          If no such Family exists, a new Family is added to the database. 
 917           
 918          @return: Returns a tuple, first the object, second a bool which is True 
 919                   if the object is new 
 920          @rtype: tuple 
 921          """ 
 922          return self.find_from_handle(handle, transaction, Family,  
 923                                       self.family_map, self.add_family) 
  924   
 926          """ 
 927          Find a Repository in the database from the passed handle. 
 928           
 929          If no such Repository exists, a new Repository is added to the database. 
 930           
 931          @return: Returns a tuple, first the object, second a bool which is True 
 932                   if the object is new 
 933          @rtype: tuple 
 934          """ 
 935          return self.find_from_handle(handle, transaction, Repository,  
 936                                       self.repository_map, self.add_repository) 
  937   
 939          """ 
 940          Find a Note in the database from the passed handle. 
 941           
 942          If no such Note exists, a new Note is added to the database. 
 943           
 944          @return: Returns a tuple, first the object, second a bool which is True 
 945                   if the object is new 
 946          @rtype: tuple 
 947          """ 
 948          return self.find_from_handle(handle, transaction, Note,  
 949                                       self.note_map, self.add_note) 
  950   
 952          """ 
 953          Check whether a Person with the passed handle exists in the database. 
 954           
 955          If no such Person exists, a new Person is added to the database. 
 956          If set_gid then a new gramps_id is created, if not, None is used. 
 957          """ 
 958          self.__check_from_handle(handle, transaction, Person,  
 959                                   self.person_map, self.add_person,  
 960                                   set_gid = set_gid) 
  961   
 963          """ 
 964          Check whether a Source with the passed handle exists in the database. 
 965           
 966          If no such Source exists, a new Source is added to the database. 
 967          If set_gid then a new gramps_id is created, if not, None is used. 
 968          """ 
 969          self.__check_from_handle(handle, transaction, Source,  
 970                                   self.source_map, self.add_source,  
 971                                   set_gid=set_gid) 
  972                                   
 974          """ 
 975          Check whether an Event with the passed handle exists in the database. 
 976           
 977          If no such Event exists, a new Event is added to the database. 
 978          If set_gid then a new gramps_id is created, if not, None is used. 
 979          """ 
 980          self.__check_from_handle(handle, transaction, Event,  
 981                                   self.event_map, self.add_event,  
 982                                   set_gid=set_gid) 
  983   
 985          """ 
 986          Check whether a MediaObject with the passed handle exists in the  
 987          database.  
 988           
 989          If no such MediaObject exists, a new Object is added to the database. 
 990          If set_gid then a new gramps_id is created, if not, None is used. 
 991          """ 
 992   
 993          self.__check_from_handle(handle, transaction, MediaObject,  
 994                                   self.media_map, self.add_object,  
 995                                   set_gid=set_gid) 
  996   
 998          """ 
 999          Check whether a Place with the passed handle exists in the database. 
1000           
1001          If no such Place exists, a new Place is added to the database. 
1002          If set_gid then a new gramps_id is created, if not, None is used. 
1003          """ 
1004          self.__check_from_handle(handle, transaction, Place,  
1005                                   self.place_map, self.add_place,  
1006                                   set_gid=set_gid) 
 1007   
1009          """ 
1010          Check whether a Family with the passed handle exists in the database. 
1011           
1012          If no such Family exists, a new Family is added to the database. 
1013          If set_gid then a new gramps_id is created, if not, None is used. 
1014          """ 
1015          self.__check_from_handle(handle, transaction, Family,  
1016                                   self.family_map, self.add_family,  
1017                                   set_gid=set_gid) 
 1018   
1020          """ 
1021          Check whether a Repository with the passed handle exists in the 
1022          database.  
1023           
1024          If no such Repository exists, a new Repository is added to the database. 
1025          If set_gid then a new gramps_id is created, if not, None is used. 
1026          """ 
1027          self.__check_from_handle(handle, transaction, Repository,  
1028                                   self.repository_map, self.add_repository,  
1029                                   set_gid=set_gid) 
 1030   
1032          """ 
1033          Check whether a Note with the passed handle exists in the database.  
1034           
1035          If no such Note exists, a new Note is added to the database. 
1036          If set_gid then a new gramps_id is created, if not, None is used. 
1037          """ 
1038          self.__check_from_handle(handle, transaction, Note,  
1039                                   self.note_map, self.add_note,  
1040                                   set_gid=set_gid) 
 1041   
1043          """ 
1044          Find a Person in the database from the passed GRAMPS ID. 
1045           
1046          If no such Person exists, None is returned. 
1047          Needs to be overridden by the derrived class. 
1048          """ 
1049          raise NotImplementedError 
 1050   
1052          """ 
1053          Find a Family in the database from the passed GRAMPS ID. 
1054           
1055          If no such Family exists, None is returned. 
1056          Need to be overridden by the derrived class. 
1057          """ 
1058          raise NotImplementedError 
 1059   
1061          """ 
1062          Find an Event in the database from the passed GRAMPS ID. 
1063           
1064          If no such Event exists, None is returned. 
1065          Needs to be overridden by the derrived class. 
1066          """ 
1067          raise NotImplementedError 
 1068   
1070          """ 
1071          Find a Place in the database from the passed gramps' ID. 
1072           
1073          If no such Place exists, None is returned. 
1074          Needs to be overridden by the derrived class. 
1075          """ 
1076          raise NotImplementedError 
 1077   
1079          """ 
1080          Find a Source in the database from the passed gramps' ID. 
1081           
1082          If no such Source exists, None is returned. 
1083          Needs to be overridden by the derrived class. 
1084          """ 
1085          raise NotImplementedError 
 1086   
1088          """ 
1089          Find a MediaObject in the database from the passed gramps' ID. 
1090   
1091          If no such MediaObject exists, None is returned. 
1092          Needs to be overridden by the derrived class. 
1093          """ 
1094          raise NotImplementedError 
 1095   
1097          """ 
1098          Find a Repository in the database from the passed gramps' ID. 
1099   
1100          If no such Repository exists, None is returned. 
1101          Needs to be overridden by the derrived class. 
1102          """ 
1103          raise NotImplementedError 
 1104   
1106          """ 
1107          Find a Note in the database from the passed gramps' ID. 
1108   
1109          If no such Note exists, None is returned. 
1110          Needs to be overridden by the derrived class. 
1111          """ 
1112          raise NotImplementedError 
 1113   
1114 -    def __add_object(self, obj, transaction, find_next_func, commit_func): 
 1115          if find_next_func and not obj.gramps_id: 
1116              obj.gramps_id = find_next_func() 
1117          if not obj.handle: 
1118              obj.handle = self.create_id() 
1119          commit_func(obj, transaction) 
1120          if obj.__class__.__name__ == 'Person': 
1121              self.genderStats.count_person (obj) 
1122          return obj.handle 
 1123   
1124 -    def add_person(self, person, transaction, set_gid=True): 
 1125          """ 
1126          Add a Person to the database, assigning internal IDs if they have 
1127          not already been defined. 
1128           
1129          If not set_gid, then gramps_id is not set. 
1130          """ 
1131          if set_gid: 
1132              return self.__add_object(person, transaction,  
1133                                      self.find_next_person_gramps_id,  
1134                                      self.commit_person) 
1135          else: 
1136              return self.__add_object(person, transaction,  
1137                                      None,  
1138                                      self.commit_person) 
 1139   
1140 -    def add_family(self, family, transaction, set_gid=True): 
 1141          """ 
1142          Add a Family to the database, assigning internal IDs if they have 
1143          not already been defined. 
1144           
1145          If not set_gid, then gramps_id is not set. 
1146          """ 
1147          if set_gid: 
1148              return self.__add_object(family, transaction,  
1149                                      self.find_next_family_gramps_id,  
1150                                      self.commit_family) 
1151          else: 
1152              return self.__add_object(family, transaction,  
1153                                      None,  
1154                                      self.commit_family) 
 1155   
1156 -    def add_source(self, source, transaction, set_gid=True): 
 1157          """ 
1158          Add a Source to the database, assigning internal IDs if they have 
1159          not already been defined. 
1160           
1161          If not set_gid, then gramps_id is not set. 
1162          """ 
1163          if set_gid: 
1164              return self.__add_object(source, transaction,  
1165                                      self.find_next_source_gramps_id,  
1166                                      self.commit_source) 
1167          else : 
1168              return self.__add_object(source, transaction,  
1169                                  None,  
1170                                  self.commit_source) 
 1171   
1172 -    def add_event(self, event, transaction, set_gid=True): 
 1173          """ 
1174          Add an Event to the database, assigning internal IDs if they have 
1175          not already been defined. 
1176           
1177          If not set_gid, then gramps_id is not set. 
1178          """ 
1179          if set_gid: 
1180              return self.__add_object(event, transaction,  
1181                                      self.find_next_event_gramps_id,  
1182                                      self.commit_event) 
1183          else: 
1184              return self.__add_object(event, transaction,  
1185                                      None,  
1186                                      self.commit_event) 
 1187   
1189          """ 
1190          Add an Event to the database, assigning internal IDs if they have 
1191          not already been defined. 
1192          """ 
1193          if event.type.is_custom(): 
1194              self.individual_event_names.add(str(event.type)) 
1195          return self.add_event(event, transaction) 
 1196   
1198          """ 
1199          Add an Event to the database, assigning internal IDs if they have 
1200          not already been defined. 
1201          """ 
1202          if event.type.is_custom(): 
1203              self.family_event_names.add(str(event.type)) 
1204          return self.add_event(event, transaction) 
 1205   
1206 -    def add_place(self, place, transaction, set_gid=True): 
 1207          """ 
1208          Add a Place to the database, assigning internal IDs if they have 
1209          not already been defined. 
1210           
1211          If not set_gid, then gramps_id is not set. 
1212          """ 
1213          if set_gid: 
1214              return self.__add_object(place, transaction,  
1215                                      self.find_next_place_gramps_id,  
1216                                      self.commit_place) 
1217          else: 
1218              return self.__add_object(place, transaction,  
1219                                      None, 
1220                                      self.commit_place) 
 1221   
1222 -    def add_object(self, obj, transaction, set_gid=True): 
 1223          """ 
1224          Add a MediaObject to the database, assigning internal IDs if they have 
1225          not already been defined. 
1226           
1227          If not set_gid, then gramps_id is not set. 
1228          """ 
1229          if set_gid: 
1230              return self.__add_object(obj, transaction,  
1231                                      self.find_next_object_gramps_id,  
1232                                      self.commit_media_object) 
1233          else: 
1234              return self.__add_object(obj, transaction,  
1235                                      None,  
1236                                      self.commit_media_object) 
 1237   
1239          """ 
1240          Add a Repository to the database, assigning internal IDs if they have 
1241          not already been defined. 
1242           
1243          If not set_gid, then gramps_id is not set. 
1244          """ 
1245          if set_gid: 
1246              return self.__add_object(obj, transaction,  
1247                                      self.find_next_repository_gramps_id,  
1248                                      self.commit_repository) 
1249          else: 
1250              return self.__add_object(obj, transaction,  
1251                                      None, 
1252                                      self.commit_repository) 
 1253   
1254 -    def add_note(self, obj, transaction, set_gid=True): 
 1255          """ 
1256          Add a Note to the database, assigning internal IDs if they have 
1257          not already been defined. 
1258           
1259          If not set_gid, then gramps_id is not set. 
1260          """ 
1261          if set_gid: 
1262              return self.__add_object(obj, transaction,  
1263                                       self.find_next_note_gramps_id,  
1264                                       self.commit_note) 
1265          else: 
1266              return self.__add_object(obj, transaction,  
1267                                       None,  
1268                                       self.commit_note) 
 1269   
1271          """ 
1272          Return the default grouping name for a surname. 
1273          """ 
1274          return unicode(self.name_group.get(str(name), name)) 
 1275   
1277          """ 
1278          Return the defined names that have been assigned to a default grouping. 
1279          """ 
1280          return [unicode(k) for k in self.name_group.keys()] 
 1281   
1283          """ 
1284          Return if a key exists in the name_group table. 
1285          """ 
1286          return self.name_group.has_key(str(name)) 
 1287   
1289          """ 
1290          Set the default grouping name for a surname.  
1291           
1292          Needs to be overridden in the derived class. 
1293          """ 
1294          raise NotImplementedError 
 1295   
1297          """ 
1298          Return the number of people currently in the database. 
1299          """ 
1300          if self.db_is_open: 
1301              return len(self.person_map) 
1302          else: 
1303              return 0 
 1304   
1306          """ 
1307          Return the number of families currently in the database. 
1308          """ 
1309          return len(self.family_map) 
 1310   
1312          """ 
1313          Return the number of events currently in the database. 
1314          """ 
1315          return len(self.event_map) 
 1316   
1318          """ 
1319          Return the number of places currently in the database. 
1320          """ 
1321          return len(self.place_map) 
 1322   
1324          """ 
1325          Return the number of sources currently in the database. 
1326          """ 
1327          return len(self.source_map) 
 1328   
1334   
1336          """ 
1337          Return the number of source repositories currently in the database. 
1338          """ 
1339          return len(self.repository_map) 
 1340   
1342          """ 
1343          Return the number of notes currently in the database. 
1344          """ 
1345          return len(self.note_map) 
 1346   
1349   
1351          """ 
1352          Return a list of database handles, one handle for each Person in 
1353          the database.  
1354           
1355          If sort_handles is True, the list is sorted by surnames. 
1356          """ 
1357          if self.db_is_open: 
1358              if sort_handles: 
1359                  slist = [] 
1360                  cursor = self.get_person_cursor() 
1361                  data = cursor.first() 
1362                  while data: 
1363                      slist.append((data[1][3][3], data[0])) 
1364                      data = cursor.next() 
1365                  cursor.close() 
1366                  slist.sort() 
1367                  return map(lambda x: x[1], slist) 
1368              else: 
1369                  return self.all_handles(self.person_map) 
1370          return [] 
 1371   
1373          """ 
1374          Return a list of database handles, one handle for each Place in 
1375          the database.  
1376           
1377          If sort_handles is True, the list is sorted by Place title. 
1378          """ 
1379          if self.db_is_open: 
1380              if sort_handles: 
1381                  slist = [] 
1382                  cursor = self.get_place_cursor() 
1383                  data = cursor.first() 
1384                  while data: 
1385                      slist.append((data[1][2], data[0])) 
1386                      data = cursor.next() 
1387                  cursor.close() 
1388                  slist.sort() 
1389                  val = map(lambda x: x[1], slist) 
1390                  return val 
1391              else: 
1392                  return self.all_handles(self.place_map) 
1393          return [] 
 1394   
1396          """ 
1397          Return a list of database handles, one handle for each Source in 
1398          the database. 
1399           
1400           If sort_handles is True, the list is sorted by Source title. 
1401          """ 
1402          if self.db_is_open: 
1403              handle_list = self.all_handles(self.source_map) 
1404              if sort_handles: 
1405                  handle_list.sort(self.__sortbysource) 
1406              return handle_list 
1407          return [] 
 1408   
1422   
1424          """ 
1425          Return a list of database handles, one handle for each Event in the  
1426          database.  
1427          """ 
1428          if self.db_is_open: 
1429              return self.all_handles(self.event_map) 
1430          return [] 
 1431   
1433          """ 
1434          Return a list of database handles, one handle for each Family in 
1435          the database. 
1436          """ 
1437          if self.db_is_open: 
1438              return self.all_handles(self.family_map) 
1439          return [] 
 1440   
1442          """ 
1443          Return a list of database handles, one handle for each Repository in 
1444          the database. 
1445          """ 
1446          if self.db_is_open: 
1447              return self.all_handles(self.repository_map) 
1448          return [] 
 1449   
1451          """ 
1452          Return a list of database handles, one handle for each Note in the  
1453          database. 
1454          """ 
1455          if self.db_is_open: 
1456              return self.all_handles(self.note_map) 
1457          return [] 
 1458   
1473   
1488   
1496   
1498          if val: 
1499              try: 
1500                  junk = val % 1 
1501                  prefix_var = val     
1502              except: 
1503                  try: 
1504                      val = val + "%d" 
1505                      junk = val % 1 
1506                      prefix_var = val     
1507                  except: 
1508                      prefix_var = default+"%04d"  
1509          else: 
1510              prefix_var = default+"%04d" 
1511          return prefix_var 
 1512       
1514          """ 
1515          Set the naming template for GRAMPS Person ID values.  
1516           
1517          The string is expected to be in the form of a simple text string, or  
1518          in a format that contains a C/Python style format string using %d,  
1519          such as I%d or I%04d. 
1520          """ 
1521          self.person_prefix = self._validated_id_prefix(val, "I") 
 1522               
1524          """ 
1525          Set the naming template for GRAMPS Source ID values.  
1526           
1527          The string is expected to be in the form of a simple text string, or  
1528          in a format that contains a C/Python style format string using %d,  
1529          such as S%d or S%04d. 
1530          """ 
1531          self.source_prefix = self._validated_id_prefix(val, "S") 
 1532               
1534          """ 
1535          Set the naming template for GRAMPS MediaObject ID values.  
1536           
1537          The string is expected to be in the form of a simple text string, or  
1538          in a format that contains a C/Python style format string using %d,  
1539          such as O%d or O%04d. 
1540          """ 
1541          self.mediaobject_prefix = self._validated_id_prefix(val, "O") 
 1542   
1544          """ 
1545          Set the naming template for GRAMPS Place ID values.  
1546           
1547          The string is expected to be in the form of a simple text string, or  
1548          in a format that contains a C/Python style format string using %d,  
1549          such as P%d or P%04d. 
1550          """ 
1551          self.place_prefix = self._validated_id_prefix(val, "P") 
 1552   
1554          """ 
1555          Set the naming template for GRAMPS Family ID values. The string is 
1556          expected to be in the form of a simple text string, or in a format 
1557          that contains a C/Python style format string using %d, such as F%d 
1558          or F%04d. 
1559          """ 
1560          self.family_prefix = self._validated_id_prefix(val, "F") 
 1561   
1563          """ 
1564          Set the naming template for GRAMPS Event ID values.  
1565           
1566          The string is expected to be in the form of a simple text string, or  
1567          in a format that contains a C/Python style format string using %d,  
1568          such as E%d or E%04d. 
1569          """ 
1570          self.event_prefix = self._validated_id_prefix(val, "E") 
 1571   
1573          """ 
1574          Set the naming template for GRAMPS Repository ID values.  
1575           
1576          The string is expected to be in the form of a simple text string, or  
1577          in a format that contains a C/Python style format string using %d,  
1578          such as R%d or R%04d. 
1579          """ 
1580          self.repository_prefix = self._validated_id_prefix(val, "R") 
 1581   
1583          """ 
1584          Set the naming template for GRAMPS Note ID values.  
1585           
1586          The string is expected to be in the form of a simple text string, or  
1587          in a format that contains a C/Python style format string using %d,  
1588          such as N%d or N%04d. 
1589          """ 
1590          self.note_prefix = self._validated_id_prefix(val, "N") 
 1591   
1593          """ 
1594          Create a new Transaction tied to the current UNDO database.  
1595           
1596          The transaction has no effect until it is committed using the 
1597          transaction_commit function of the this database object. 
1598          """ 
1599          if self._LOG_ALL: 
1600              LOG.debug("%s: Transaction begin '%s'\n" 
1601                        % (self.__class__.__name__, str(msg))) 
1602          if batch: 
1603               
1604               
1605              self.abort_possible = False 
1606              self.undo_history_timestamp = time.time() 
1607               
1608              self.undoindex = -1 
1609              self.translist  = [None] * _UNDO_SIZE 
1610          return Transaction(msg, self.undodb, batch) 
 1611   
1613          """ 
1614          Commit the transaction to the assocated UNDO database. 
1615          """ 
1616          if self._LOG_ALL: 
1617              LOG.debug("%s: Transaction commit '%s'\n" 
1618                        % (self.__class__.__name__, str(msg))) 
1619   
1620          if not len(transaction) or self.readonly: 
1621              return 
1622   
1623          transaction.set_description(msg) 
1624          transaction.timestamp = time.time() 
1625          self.undoindex += 1                     
1626          if self.undoindex >= _UNDO_SIZE: 
1627               
1628               
1629              self.abort_possible = False 
1630              self.undo_history_timestamp = time.time()             
1631              self.translist = self.translist[0:-1] + [ transaction ] 
1632          else: 
1633              self.translist[self.undoindex] = transaction 
1634               
1635               
1636              for index in range(self.undoindex+1, _UNDO_SIZE): 
1637                  self.translist[index] = None 
1638   
1639          person_add = self.do_commit(transaction.person_add, self.person_map) 
1640          family_add = self.do_commit(transaction.family_add, self.family_map) 
1641          source_add = self.do_commit(transaction.source_add, self.source_map) 
1642          place_add = self.do_commit(transaction.place_add, self.place_map) 
1643          media_add = self.do_commit(transaction.media_add, self.media_map) 
1644          event_add = self.do_commit(transaction.event_add, self.event_map) 
1645          repository_add = self.do_commit(transaction.repository_add,  
1646                                          self.repository_map) 
1647   
1648          note_add = self.do_commit(transaction.note_add, self.note_map) 
1649          person_upd = self.do_commit(transaction.person_update, self.person_map) 
1650          family_upd = self.do_commit(transaction.family_update, self.family_map) 
1651          source_upd = self.do_commit(transaction.source_update, self.source_map) 
1652          place_upd = self.do_commit(transaction.place_update, self.place_map) 
1653          media_upd = self.do_commit(transaction.media_update, self.media_map) 
1654          event_upd = self.do_commit(transaction.event_update, self.event_map) 
1655          repository_upd = self.do_commit(transaction.repository_update,  
1656                                          self.repository_map) 
1657          note_upd = self.do_commit(transaction.note_update, self.note_map) 
1658   
1659          self.__do_emit('person', person_add, person_upd, transaction.person_del) 
1660          self.__do_emit('family', family_add, family_upd, transaction.family_del) 
1661          self.__do_emit('event',  event_add,  event_upd,  transaction.event_del) 
1662          self.__do_emit('source', source_add, source_upd, transaction.source_del) 
1663          self.__do_emit('place',  place_add,  place_upd,  transaction.place_del) 
1664          self.__do_emit('media',  media_add,  media_upd,  transaction.media_del) 
1665          self.__do_emit('repository', repository_add, repository_upd,  
1666                        transaction.repository_del) 
1667          self.__do_emit('note',   note_add,   note_upd,   transaction.note_del) 
1668   
1669          self.__do_del(transaction.person_del,     self.del_person) 
1670          self.__do_del(transaction.family_del,     self.del_family) 
1671          self.__do_del(transaction.place_del,      self.del_place) 
1672          self.__do_del(transaction.source_del,     self.del_source) 
1673          self.__do_del(transaction.event_del,      self.del_event) 
1674          self.__do_del(transaction.media_del,      self.del_media) 
1675          self.__do_del(transaction.repository_del, self.del_repository) 
1676          self.__do_del(transaction.note_del,       self.del_note) 
1677   
1678          if self.undo_callback: 
1679              self.undo_callback(_("_Undo %s") % transaction.get_description()) 
1680          if self.redo_callback: 
1681              self.redo_callback(None) 
1682          if self.undo_history_callback: 
1683              self.undo_history_callback() 
 1684   
1685 -    def __do_emit(self, objtype, add_list, upd_list, del_list): 
 1686          if add_list: 
1687              self.emit(objtype + '-add', (add_list, )) 
1688          if upd_list: 
1689              self.emit(objtype + '-update', (upd_list, )) 
1690          if del_list: 
1691              self.emit(objtype + '-delete', (del_list, )) 
 1692   
1694          for handle in del_list: 
1695              func(handle) 
1696          return del_list 
 1697   
1699          retlist = [] 
1700          for (handle, data) in add_list: 
1701              db_map[handle] = data 
1702              retlist.append(str(handle)) 
1703          return retlist 
 1704   
1706          """ 
1707          Return boolean of whether or not there's a possibility of undo. 
1708          """ 
1709          if self.undoindex == -1 or self.readonly: 
1710              return False 
1711          return True 
 1712           
1714          """ 
1715          Return boolean of whether or not there's a possibility of redo. 
1716          """ 
1717          if self.undoindex >= _UNDO_SIZE or self.readonly: 
1718              return False 
1719   
1720          if self.translist[self.undoindex+1] == None: 
1721              return False 
1722   
1723          return True 
 1724           
1725 -    def undo(self, update_history=True): 
 1726          """ 
1727          Access the last committed transaction, and revert the data to the  
1728          state before the transaction was committed. 
1729          """ 
1730          if not self.undo_available(): 
1731              return False 
1732   
1733          transaction = self.translist[self.undoindex] 
1734   
1735          mapbase = (self.person_map, self.family_map, self.source_map,  
1736                     self.event_map, self.media_map, self.place_map, 
1737                     self.repository_map, {}, self.note_map) 
1738   
1739          self.undoindex -= 1 
1740          subitems = transaction.get_recnos() 
1741          subitems.reverse() 
1742          for record_id in subitems: 
1743              (key, handle, old_data, new_data) = transaction.get_record(record_id) 
1744              if key == REFERENCE_KEY: 
1745                  self.undo_reference(old_data, handle) 
1746              else: 
1747                  self.undo_data(old_data, handle, mapbase[key], _SIGBASE[key]) 
1748   
1749          if self.undo_callback: 
1750              if self.undo_available(): 
1751                  new_transaction = self.translist[self.undoindex] 
1752                  self.undo_callback(_("_Undo %s") 
1753                                     % new_transaction.get_description()) 
1754              else: 
1755                  self.undo_callback(None) 
1756   
1757          if self.redo_callback: 
1758              if self. redo_available(): 
1759                  self.redo_callback(_("_Redo %s") 
1760                                     % transaction.get_description()) 
1761              else: 
1762                  self.redo_callback(None) 
1763   
1764          if update_history and self.undo_history_callback: 
1765              self.undo_history_callback() 
1766          return True 
 1767   
1768 -    def redo(self, update_history=True): 
 1769          """ 
1770          Accesse the last undone transaction, and revert the data to the state  
1771          before the transaction was undone. 
1772          """ 
1773   
1774          if not self.redo_available(): 
1775              return False 
1776   
1777          self.undoindex += 1 
1778          transaction = self.translist[self.undoindex] 
1779          mapbase = (self.person_map, self.family_map, self.source_map,  
1780                     self.event_map, self.media_map, self.place_map, 
1781                     self.repository_map, {}, self.note_map) 
1782   
1783          subitems = transaction.get_recnos() 
1784          for record_id in subitems: 
1785              (key, handle, old_data, new_data) = transaction.get_record(record_id) 
1786              if key == REFERENCE_KEY: 
1787                  self.undo_reference(new_data, handle) 
1788              else: 
1789                  self.undo_data(new_data, handle, mapbase[key], _SIGBASE[key]) 
1790   
1791          if self.undo_callback: 
1792              if self.undo_available(): 
1793                  self.undo_callback(_("_Undo %s") 
1794                                     % transaction.get_description()) 
1795              else: 
1796                  self.undo_callback(None) 
1797   
1798          if self.redo_callback: 
1799              if self.redo_available(): 
1800                  new_transaction = self.translist[self.undoindex+1] 
1801                  self.redo_callback(_("_Redo %s") 
1802                                     % new_transaction.get_description()) 
1803              else: 
1804                  self.redo_callback(None) 
1805   
1806          if update_history and self.undo_history_callback: 
1807              self.undo_history_callback() 
1808          return True 
 1809   
1812   
1813 -    def undo_data(self, data, handle, db_map, signal_root): 
 1814          if data == None: 
1815              self.emit(signal_root + '-delete', ([handle], )) 
1816              del db_map[handle] 
1817          else: 
1818              if db_map.has_key(handle): 
1819                  signal = signal_root + '-update' 
1820              else: 
1821                  signal = signal_root + '-add' 
1822              db_map[handle] = data 
1823              self.emit(signal, ([handle], )) 
 1824       
1826          """ 
1827          Define the callback function that is called whenever an undo operation 
1828          is executed.  
1829           
1830          The callback function receives a single argument that is a text string  
1831          that defines the operation. 
1832          """ 
1833          self.undo_callback = callback 
 1834   
1836          """ 
1837          Define the callback function that is called whenever an redo operation 
1838          is executed.  
1839           
1840          The callback function receives a single argument that is a text string  
1841          that defines the operation. 
1842          """ 
1843          self.redo_callback = callback 
 1844   
1846          """ 
1847          Return the list of locale-sorted surnames contained in the database. 
1848          """ 
1849          return self.surname_list 
 1850   
1852          """ 
1853          Build the list of locale-sorted surnames contained in the database. 
1854           
1855          The function must be overridden in the derived class. 
1856          """ 
1857          raise NotImplementedError 
 1858   
1860          vals = [(locale.strxfrm(item), item) for item in self.surname_list] 
1861          vals.sort() 
1862          self.surname_list = [item[1] for item in vals] 
 1863   
1871   
1873          """ 
1874          Check whether there are persons with the same surname left in 
1875          the database.  
1876           
1877          If not then we need to remove the name from the list. 
1878          The function must be overridden in the derived class. 
1879          """ 
1880          raise NotImplementedError 
 1881   
1883          """Return the list of Person handles in the bookmarks.""" 
1884          return self.bookmarks 
 1885   
1887          """Return the list of Person handles in the bookmarks.""" 
1888          return self.family_bookmarks 
 1889   
1891          """Return the list of Person handles in the bookmarks.""" 
1892          return self.event_bookmarks 
 1893   
1895          """Return the list of Person handles in the bookmarks.""" 
1896          return self.place_bookmarks 
 1897   
1899          """Return the list of Person handles in the bookmarks.""" 
1900          return self.source_bookmarks 
 1901   
1905   
1907          """Return the list of Person handles in the bookmarks.""" 
1908          return self.repo_bookmarks 
 1909   
1911          """Return the list of Note handles in the bookmarks.""" 
1912          return self.note_bookmarks 
 1913   
1915          """Set the information about the owner of the database.""" 
1916          self.owner.set_from(owner) 
 1917   
1919          """ 
1920          Return the Researcher instance, providing information about the owner  
1921          of the database. 
1922          """ 
1923          return self.owner 
 1924   
1926          """Set the default Person to the passed instance.""" 
1927          if (self.metadata != None) and (not self.readonly): 
1928              self.metadata['default'] = str(handle) 
 1929   
1938   
1940          """Return the default Person of the database.""" 
1941          if self.metadata != None: 
1942              return self.metadata.get('default') 
1943          return None 
 1944   
1946          """Return the save path of the file, or "" if one does not exist.""" 
1947          return self.path 
 1948   
1950          """Set the save path for the database.""" 
1951          self.path = path 
 1952   
1954          """ 
1955          Return a list of all Event types assocated with Person instances in  
1956          the database. 
1957          """ 
1958          return list(self.individual_event_names) 
 1959   
1961          """ 
1962          Return a list of all Attribute types assocated with Person instances  
1963          in the database. 
1964          """ 
1965          return list(self.individual_attributes) 
 1966   
1968          """ 
1969          Return a list of all Attribute types assocated with Family instances  
1970          in the database. 
1971          """ 
1972          return list(self.family_attributes) 
 1973   
1975          """ 
1976          Return a list of all Event types assocated with Family instances in  
1977          the database. 
1978          """ 
1979          return list(self.family_event_names) 
 1980   
1982          """ 
1983          Return a list of all marker types available in the database. 
1984          """ 
1985          return list(self.marker_names) 
 1986           
1993   
1995          """ 
1996          Return a list of all relationship types assocated with Family 
1997          instances in the database. 
1998          """ 
1999          return list(self.family_rel_types) 
 2000   
2002          """ 
2003          Return a list of all child reference types assocated with Family 
2004          instances in the database. 
2005          """ 
2006          return list(self.child_ref_types) 
 2007   
2009          """ 
2010          Return a list of all custom event role names assocated with Event 
2011          instances in the database. 
2012          """ 
2013          return list(self.event_role_names) 
 2014   
2016          """ 
2017          Return a list of all custom names types assocated with Person 
2018          instances in the database. 
2019          """ 
2020          return list(self.name_types) 
 2021   
2023          """ 
2024          Return a list of all custom repository types assocated with Repository  
2025          instances in the database. 
2026          """ 
2027          return list(self.repository_types) 
 2028   
2030          """ 
2031          Return a list of all custom note types assocated with Note instances  
2032          in the database. 
2033          """ 
2034          return list(self.note_types) 
 2035   
2042   
2044          """ 
2045          Return a list of all custom names types assocated with Url instances  
2046          in the database. 
2047          """ 
2048          return list(self.url_types) 
 2049   
2069   
2082   
2084          if self.readonly or not handle: 
2085              return 
2086   
2087          handle = str(handle) 
2088          self.delete_primary_from_reference_map(handle, trans) 
2089          if trans.batch: 
2090              del_func = self.get_del_func(key) 
2091              del_func(handle) 
2092          else: 
2093              old_data = dmap.get(handle) 
2094              trans.add(key, handle, old_data, None) 
2095              del_list.append(handle) 
 2096   
2098          """ 
2099          Remove the Source specified by the database handle from the 
2100          database, preserving the change in the passed transaction.  
2101           
2102          This method must be overridden in the derived class. 
2103          """ 
2104          self.do_remove_object(handle, transaction, self.source_map,  
2105                                SOURCE_KEY, transaction.source_del) 
 2106   
2108          """ 
2109          Remove the Event specified by the database handle from the 
2110          database, preserving the change in the passed transaction.  
2111           
2112          This method must be overridden in the derived class. 
2113          """ 
2114          self.do_remove_object(handle, transaction, self.event_map,  
2115                                EVENT_KEY, transaction.event_del) 
 2116   
2118          """ 
2119          Remove the MediaObjectPerson specified by the database handle from the 
2120          database, preserving the change in the passed transaction.  
2121           
2122          This method must be overridden in the derived class. 
2123          """ 
2124          self.do_remove_object(handle, transaction, self.media_map,  
2125                                MEDIA_KEY, transaction.media_del) 
 2126   
2128          """ 
2129          Remove the Place specified by the database handle from the 
2130          database, preserving the change in the passed transaction.  
2131           
2132          This method must be overridden in the derived class. 
2133          """ 
2134          self.do_remove_object(handle, transaction, self.place_map,  
2135                                PLACE_KEY, transaction.place_del) 
 2136   
2138          """ 
2139          Remove the Family specified by the database handle from the 
2140          database, preserving the change in the passed transaction.  
2141           
2142          This method must be overridden in the derived class. 
2143          """ 
2144          self.do_remove_object(handle, transaction, self.family_map,  
2145                                FAMILY_KEY, transaction.family_del) 
 2146   
2148          """ 
2149          Remove the Repository specified by the database handle from the 
2150          database, preserving the change in the passed transaction.  
2151           
2152          This method must be overridden in the derived class. 
2153          """ 
2154          self.do_remove_object(handle, transaction, self.repository_map,  
2155                                REPOSITORY_KEY, transaction.repository_del) 
 2156   
2158          """ 
2159          Remove the Note specified by the database handle from the 
2160          database, preserving the change in the passed transaction.  
2161           
2162          This method must be overridden in the derived class. 
2163          """ 
2164          self.do_remove_object(handle, transaction, self.note_map,  
2165                                NOTE_KEY, transaction.note_del) 
 2166   
2168          return self.person_map.get(str(handle)) 
 2169   
2171          return self.family_map.get(str(handle)) 
 2172   
2174          return self.media_map.get(str(handle)) 
 2175   
2177          return self.place_map.get(str(handle)) 
 2178   
2180          return self.event_map.get(str(handle)) 
 2181   
2183          return self.source_map.get(str(handle)) 
 2184   
2186          return self.repository_map.get(str(handle)) 
 2187   
2189          return self.note_map.get(str(handle)) 
 2190   
2192          """ 
2193          Return True if the handle exists in the current Person database. 
2194          """ 
2195          return self.person_map.has_key(str(handle)) 
 2196   
2198          """ 
2199          Return True if the handle exists in the current Event database. 
2200          """ 
2201          return self.event_map.has_key(str(handle)) 
 2202   
2204          """ 
2205          Return True if the handle exists in the current Source database. 
2206          """ 
2207          return self.source_map.has_key(str(handle)) 
 2208   
2210          """ 
2211          Return True if the handle exists in the current Place database. 
2212          """ 
2213          return self.place_map.has_key(str(handle)) 
 2214   
2216          """ 
2217          Return True if the handle exists in the current Family database. 
2218          """ 
2219          return self.family_map.has_key(str(handle)) 
 2220   
2222          """ 
2223          Return True if the handle exists in the current MediaObjectdatabase. 
2224          """ 
2225          return self.media_map.has_key(str(handle)) 
 2226   
2228          """ 
2229          Return True if the handle exists in the current Repository database. 
2230          """ 
2231          return self.repository_map.has_key(str(handle)) 
 2232   
2234          """ 
2235          Return True if the handle exists in the current Note database. 
2236          """ 
2237          return self.note_map.has_key(str(handle)) 
 2238   
2240          return locale.strcoll(self.place_map.get(str(first))[2],  
2241                                self.place_map.get(str(second))[2]) 
 2242   
2244          source1 = unicode(self.source_map[str(first)][2]) 
2245          source2 = unicode(self.source_map[str(second)][2]) 
2246          return locale.strcoll(source1, source2) 
 2247   
2252   
2257   
2263   
2265          if (self.metadata != None) and (not self.readonly): 
2266              self.metadata[name] = col_list 
 2267   
2273   
2279   
2281          """ 
2282          Store the Person display common information in the database's metadata. 
2283          """ 
2284          self.set_column_order(col_list, CHILD_COL_KEY) 
 2285   
2287          """ 
2288          Store the Place display common information in the database's metadata. 
2289          """ 
2290          self.set_column_order(col_list, PLACE_COL_KEY) 
 2291   
2297   
2303   
2305          """ 
2306          Store the Event display common information in the database's metadata. 
2307          """ 
2308          self.set_column_order(col_list, EVENT_COL_KEY) 
 2309   
2316   
2318          """ 
2319          Store the Note display common information in the database's metadata. 
2320          """ 
2321          self.set_column_order(col_list, NOTE_COL_KEY) 
 2322   
2324          if self.metadata == None: 
2325              return default 
2326          else: 
2327              cols = self.metadata.get(name, default) 
2328              if len(cols) != len(default): 
2329                  return cols + default[len(cols):] 
2330              else: 
2331                  return cols 
 2332   
2334          """ 
2335          Return the Person display common information stored in the database's  
2336          metadata. 
2337          """ 
2338          default = [(1, 1, 100), (1, 2, 100), (1, 3, 150), (0, 4, 150), 
2339                     (1, 5, 150), (0, 6, 150), (0, 7, 100), (0, 8, 100), 
2340                     ] 
2341          return self.__get_column_order(PERSON_COL_KEY, default) 
 2342   
2344          values = self.__get_column_order(key, default) 
2345          new = [] 
2346          for val in values: 
2347              if len(val) == 2: 
2348                  for x in default: 
2349                      if val[1] == x[1]: 
2350                          new.append((val[0], val[1], x[2])) 
2351                          break 
2352              else: 
2353                  new.append(val) 
2354          return new 
 2355           
2357          """ 
2358          Return the Person display common information stored in the database's  
2359          metadata. 
2360          """ 
2361          default = [(1, 0, 75), (1, 1, 200), (1, 2, 200), (1, 3, 100), 
2362                     (0, 4, 100)] 
2363          return self.__get_columns(FAMILY_COL_KEY, default) 
 2364   
2366          """ 
2367          Return the Person display common information stored in the database's  
2368          metadata. 
2369          """ 
2370          default = [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), 
2371                     (0, 6), (0, 7)] 
2372          return self.__get_column_order(CHILD_COL_KEY, default) 
 2373   
2375          """ 
2376          Return the Place display common information stored in thedatabase's  
2377          metadata. 
2378          """ 
2379          default = [(1, 0, 250), (1, 1, 75), (1, 11, 100), (0, 3, 100), 
2380                     (1, 4, 100, ), (0, 5, 150), (1, 6, 150), (0, 7, 150), 
2381                     (0, 8, 150), (0, 9, 150), (0, 10, 150),(0,2,100)] 
2382          return self.__get_columns(PLACE_COL_KEY, default) 
 2383   
2385          """ 
2386          Return the Source display common information stored in the database's  
2387          metadata. 
2388          """ 
2389          default = [(1, 0, 200), (1, 1, 75), (1, 2, 150), (0, 3, 100), 
2390                     (1, 4, 150), (0, 5, 100)] 
2391          return self.__get_columns(SOURCE_COL_KEY, default) 
 2392   
2401   
2403          """ 
2404          Return the Event display common information stored in the database's  
2405          metadata. 
2406          """ 
2407          default = [(1, 0, 200), (1, 1, 75), (1, 2, 100), (1, 3, 150), 
2408                     (1, 4, 200), (0, 5, 100)] 
2409          return self.__get_columns(EVENT_COL_KEY, default) 
 2410   
2412          """ 
2413          Return the Repository display common information stored in the 
2414          database's metadata. 
2415          """ 
2416          default = [(1, 0, 200), (1, 1, 75), (0, 5, 100), (0, 6, 100), 
2417                     (1, 2, 100), (1, 3, 250), (1, 4, 100), (0, 7, 100), 
2418                     (0, 8, 100), (0, 9, 100), (0, 10, 100), (0, 12, 100)] 
2419          return self.__get_columns(REPOSITORY_COL_KEY, default) 
 2420   
2422          """ 
2423          Return the Note display common information stored in the database's  
2424          metadata. 
2425          """ 
2426          default = [(1, 0, 350), (1, 1, 75), (1, 2, 100), (1, 3, 100)] 
2427          return self.__get_columns(NOTE_COL_KEY, default) 
 2428   
2430          """ 
2431          Called each time an object is removed from the database.  
2432           
2433          This can be used by subclasses to update any additional index tables  
2434          that might need to be changed. 
2435          """ 
2436          pass 
 2437   
2439          """ 
2440          Called each time an object is writen to the database.  
2441           
2442          This can be used by subclasses to update any additional index tables  
2443          that might need to be changed. 
2444          """ 
2445          pass 
 2446   
2448          """ 
2449          Reindex all primary records in the database. 
2450          """ 
2451          pass 
 2452   
2454          """ 
2455          Find all objects that hold a reference to the object handle. 
2456           
2457          Returns an interator over alist of (class_name, handle) tuples. 
2458   
2459          @param handle: handle of the object to search for. 
2460          @type handle: database handle 
2461          @param include_classes: list of class names to include in the results. 
2462                                  Default: None means include all classes. 
2463          @type include_classes: list of class names 
2464           
2465          This default implementation does a sequencial scan through all 
2466          the primary object databases and is very slow. Backends can 
2467          override this method to provide much faster implementations that 
2468          make use of additional capabilities of the backend. 
2469   
2470          Note that this is a generator function, it returns a iterator for 
2471          use in loops. If you want a list of the results use: 
2472   
2473          >    result_list = [i for i in find_backlink_handles(handle)] 
2474          """ 
2475   
2476           
2477           
2478          primary_tables = { 
2479              'Person': {'cursor_func': self.get_person_cursor,  
2480                         'class_func': Person},  
2481              'Family': {'cursor_func': self.get_family_cursor,  
2482                         'class_func': Family},  
2483              'Event': {'cursor_func': self.get_event_cursor,  
2484                        'class_func': Event},  
2485              'Place': {'cursor_func': self.get_place_cursor,  
2486                        'class_func': Place},  
2487              'Source': {'cursor_func': self.get_source_cursor,  
2488                         'class_func': Source},  
2489              'MediaObject': {'cursor_func': self.get_media_cursor,  
2490                              'class_func': MediaObject},  
2491              'Repository': {'cursor_func': self.get_repository_cursor,  
2492                             'class_func': Repository}, 
2493              'Note':   {'cursor_func': self.get_note_cursor,  
2494                         'class_func': Note}, 
2495              } 
2496   
2497   
2498           
2499          if (include_classes == None): 
2500              the_tables = primary_tables.keys() 
2501          else: 
2502              the_tables = include_classes 
2503           
2504           
2505           
2506          for primary_table_name in the_tables: 
2507              cursor = primary_tables[primary_table_name]['cursor_func']() 
2508              data = cursor.first() 
2509   
2510               
2511               
2512              class_func = primary_tables[primary_table_name]['class_func'] 
2513   
2514              while data: 
2515                  found_handle, val = data 
2516                  obj = class_func() 
2517                  obj.unserialize(val) 
2518   
2519                   
2520                   
2521                  for classname in primary_tables.keys():                
2522                      if obj.has_handle_reference(classname, handle): 
2523                          yield (primary_table_name, found_handle) 
2524                           
2525                  data = cursor.next() 
2526                       
2527              cursor.close() 
2528   
2529          return 
 2530   
2532          """ 
2533          Add 1 to the number of bookmark changes during this session. 
2534          """ 
2535          self._bm_changes += 1 
 2536   
2538          """ 
2539          Return whethere there were bookmark changes during the session. 
2540          """ 
2541          return self._bm_changes > 0 
 2542           
2544      """ 
2545      Define a group of database commits that define a single logical operation. 
2546      """ 
2547 -    def __init__(self, msg, db, batch=False, no_magic=False): 
 2548          """ 
2549          Create a new transaction.  
2550           
2551          A Transaction instance should not be created directly, but by the  
2552          GrampsDbBase class or classes derived from GrampsDbBase. The db  
2553          parameter is a list-like interface that stores the commit data. This  
2554          could be a simple list, or a RECNO-style database object. 
2555   
2556          The batch parameter is set to True for large transactions. For such 
2557          transactions, the list of changes is not maintained, and no undo 
2558          is possible. 
2559   
2560          The no_magic parameter is ignored for non-batch transactions, and 
2561          is also of no importance for DB backends other than BSD DB. For 
2562          the BSDDB, when this paramter is set to True, some secondary 
2563          indices will be removed at the beginning and then rebuilt at 
2564          the end of such transaction (only if it is batch). 
2565          """ 
2566          self.db = db 
2567          self.first = None 
2568          self.last = None 
2569          self.batch = batch 
2570          self.no_magic = no_magic 
2571          self.length = 0 
2572          self.timestamp = 0 
2573   
2574          self.person_add = [] 
2575          self.person_del = [] 
2576          self.person_update = [] 
2577   
2578          self.family_add = [] 
2579          self.family_del = [] 
2580          self.family_update = [] 
2581   
2582          self.source_add = [] 
2583          self.source_del = [] 
2584          self.source_update = [] 
2585   
2586          self.event_add = [] 
2587          self.event_del = [] 
2588          self.event_update = [] 
2589   
2590          self.media_add = [] 
2591          self.media_del = [] 
2592          self.media_update = [] 
2593   
2594          self.place_add = [] 
2595          self.place_del = [] 
2596          self.place_update = [] 
2597   
2598          self.repository_add = [] 
2599          self.repository_del = [] 
2600          self.repository_update = [] 
2601   
2602          self.note_add = [] 
2603          self.note_del = [] 
2604          self.note_update = [] 
 2605   
2607          """ 
2608          Return the text string that describes the logical operation performed  
2609          by the Transaction. 
2610          """ 
2611          return self.msg 
 2612   
2614          """ 
2615          Set the text string that describes the logical operation performed by  
2616          the Transaction. 
2617          """ 
2618          self.msg = msg 
 2619   
2620 -    def add(self, obj_type, handle, old_data, new_data): 
 2621          """ 
2622          Add a commit operation to the Transaction.  
2623           
2624          The obj_type is a constant that indicates what type of PrimaryObject  
2625          is being added. The handle is the object's database handle, and the  
2626          data is the tuple returned by the object's serialize method. 
2627          """ 
2628          self.last = self.db.append( 
2629              cPickle.dumps((obj_type, handle, old_data, new_data), 1)) 
2630          if self.first == None: 
2631              self.first = self.last 
 2632   
2634          """ 
2635          Return a list of record numbers associated with the transaction. 
2636           
2637          While the list is an arbitrary index of integers, it can be used 
2638          to indicate record numbers for a database. 
2639          """ 
2640          return range (self.first, self.last+1) 
 2641   
2643          """ 
2644          Return a tuple representing the PrimaryObject type, database handle 
2645          for the PrimaryObject, and a tuple representing the data created by 
2646          the object's serialize method. 
2647          """ 
2648          return cPickle.loads(self.db[recno]) 
 2649   
2651          """ 
2652          Return the number of commits associated with the Transaction. 
2653          """ 
2654          if self.last and self.first: 
2655              return self.last - self.first + 1 
2656          return 0 
  2657