2004-08-01 09:51:31 +05:30
|
|
|
#
|
|
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
|
|
#
|
|
|
|
# Copyright (C) 2000-2004 Donald N. Allingham
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
|
|
|
|
|
|
# $Id$
|
|
|
|
|
2004-08-07 10:46:57 +05:30
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# libraries
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2004-08-01 09:51:31 +05:30
|
|
|
from RelLib import *
|
|
|
|
import cPickle
|
2004-08-07 10:46:57 +05:30
|
|
|
from gettext import gettext as _
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
2004-08-07 10:46:57 +05:30
|
|
|
# constants
|
2004-08-01 09:51:31 +05:30
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
2004-08-07 10:46:57 +05:30
|
|
|
_UNDO_SIZE = 1000
|
|
|
|
_id_reg = compile("%\d+d")
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# GrampsDbBase
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
class GrampsDbBase:
|
|
|
|
"""GRAMPS database object. This object is a base class for other
|
|
|
|
objects."""
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
"""creates a new GrampsDB"""
|
|
|
|
|
|
|
|
self.set_iprefix(GrampsCfg.get_iprefix())
|
|
|
|
self.set_oprefix(GrampsCfg.get_oprefix())
|
|
|
|
self.set_fprefix(GrampsCfg.get_fprefix())
|
|
|
|
self.set_sprefix(GrampsCfg.get_sprefix())
|
|
|
|
self.set_pprefix(GrampsCfg.get_pprefix())
|
|
|
|
self.set_eprefix(GrampsCfg.get_eprefix())
|
|
|
|
self.open = 0
|
|
|
|
self.new()
|
|
|
|
self.added_files = []
|
|
|
|
self.genderStats = GenderStats()
|
|
|
|
|
2004-08-07 10:46:57 +05:30
|
|
|
self.id_trans = None
|
2004-08-01 09:51:31 +05:30
|
|
|
self.env = None
|
|
|
|
self.person_map = None
|
|
|
|
self.family_map = None
|
|
|
|
self.place_map = None
|
|
|
|
self.source_map = None
|
|
|
|
self.media_map = None
|
|
|
|
self.event_map = None
|
|
|
|
self.eventnames = None
|
|
|
|
self.metadata = None
|
|
|
|
self.undolabel = None
|
|
|
|
self.redolabel = None
|
|
|
|
self.modified = 0
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
def new(self):
|
|
|
|
"""initializes the GrampsDB to empty values"""
|
2004-08-01 09:51:31 +05:30
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
self.undoindex = -1
|
|
|
|
self.translist = [None] * _UNDO_SIZE
|
|
|
|
self.smap_index = 0
|
|
|
|
self.emap_index = 0
|
|
|
|
self.pmap_index = 0
|
|
|
|
self.fmap_index = 0
|
|
|
|
self.lmap_index = 0
|
|
|
|
self.omap_index = 0
|
|
|
|
self.default = None
|
|
|
|
self.owner = Researcher()
|
|
|
|
self.bookmarks = []
|
|
|
|
self.path = ""
|
|
|
|
self.place2title = {}
|
|
|
|
self.genderStats = GenderStats()
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
def load(self,name,callback):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
def close(self):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def is_open(self):
|
|
|
|
return self.person_map != None
|
|
|
|
|
|
|
|
def commit_person(self,person,transaction):
|
|
|
|
handle = person.get_handle()
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.person_map.get(handle)
|
|
|
|
transaction.add(PERSON_KEY,handle,old_data)
|
|
|
|
self.person_map[handle] = person.serialize()
|
|
|
|
|
|
|
|
def commit_media_object(self,obj,transaction):
|
|
|
|
handle = str(obj.get_handle())
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.media_map.get(handle)
|
|
|
|
transaction.add(MEDIA_KEY,handle,old_data)
|
|
|
|
self.media_map[str(obj.get_handle())] = obj.serialize()
|
|
|
|
|
|
|
|
def commit_source(self,source,transaction):
|
|
|
|
handle = str(source.get_handle())
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.source_map.get(handle)
|
|
|
|
transaction.add(SOURCE_KEY,handle,old_data)
|
|
|
|
self.source_map[str(source.get_handle())] = source.serialize()
|
|
|
|
|
|
|
|
def commit_place(self,place,transaction):
|
|
|
|
handle = str(place.get_handle())
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.place_map.get(handle)
|
|
|
|
transaction.add(PLACE_KEY,handle,old_data)
|
|
|
|
self.place_map[str(place.get_handle())] = place.serialize()
|
|
|
|
|
|
|
|
def commit_event(self,event,transaction):
|
|
|
|
handle = str(event.get_handle())
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.event_map.get(handle)
|
|
|
|
transaction.add(EVENT_KEY,handle,old_data)
|
|
|
|
self.event_map[str(event.get_handle())] = event.serialize()
|
|
|
|
|
|
|
|
def commit_family(self,family,transaction):
|
|
|
|
handle = str(family.get_handle())
|
|
|
|
if transaction != None:
|
|
|
|
old_data = self.family_map.get(handle)
|
|
|
|
transaction.add(FAMILY_KEY,handle,old_data)
|
|
|
|
self.family_map[str(family.get_handle())] = family.serialize()
|
|
|
|
|
2004-08-01 09:51:31 +05:30
|
|
|
def find_next_gramps_id(self):
|
|
|
|
index = self.iprefix % self.pmap_index
|
|
|
|
while self.id_trans.get(str(index)):
|
|
|
|
self.pmap_index += 1
|
|
|
|
index = self.iprefix % self.pmap_index
|
|
|
|
self.pmap_index += 1
|
|
|
|
return index
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
def find_next_place_gramps_id(self):
|
|
|
|
index = self.pprefix % self.lmap_index
|
|
|
|
while self.id_trans.get(str(index)):
|
|
|
|
self.lmap_index += 1
|
|
|
|
index = self.pprefix % self.lmap_index
|
|
|
|
self.lmap_index += 1
|
|
|
|
return index
|
|
|
|
|
|
|
|
def find_next_event_gramps_id(self):
|
|
|
|
index = self.eprefix % self.emap_index
|
|
|
|
while self.id_trans.get(str(index)):
|
|
|
|
self.emap_index += 1
|
|
|
|
index = self.eprefix % self.emap_index
|
|
|
|
self.emap_index += 1
|
|
|
|
return index
|
|
|
|
|
|
|
|
def find_next_object_gramps_id(self):
|
|
|
|
index = self.oprefix % self.omap_index
|
|
|
|
while self.id_trans.get(str(index)):
|
|
|
|
self.omap_index += 1
|
|
|
|
index = self.oprefix % self.omap_index
|
|
|
|
self.omap_index += 1
|
|
|
|
return index
|
|
|
|
|
|
|
|
def find_next_source_gramps_id(self):
|
|
|
|
index = self.sprefix % self.smap_index
|
|
|
|
while self.source_map.get(str(index)):
|
|
|
|
self.smap_index += 1
|
|
|
|
index = self.sprefix % self.smap_index
|
|
|
|
self.fmap_index += 1
|
|
|
|
return index
|
|
|
|
|
|
|
|
def find_next_family_gramps_id(self):
|
|
|
|
index = self.fprefix % self.fmap_index
|
|
|
|
while self.family_map.get(str(index)):
|
|
|
|
self.fmap_index += 1
|
|
|
|
index = self.fprefix % self.fmap_index
|
|
|
|
self.fmap_index += 1
|
|
|
|
return index
|
|
|
|
|
|
|
|
def get_person_from_handle(self,val):
|
|
|
|
"""finds a Person in the database from the passed gramps' ID.
|
|
|
|
If no such Person exists, a new Person is added to the database."""
|
|
|
|
|
|
|
|
data = self.person_map.get(val)
|
|
|
|
if data:
|
|
|
|
person = Person()
|
|
|
|
person.unserialize(data)
|
|
|
|
return person
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_source_from_handle(self,val):
|
|
|
|
"""finds a Source in the database from the passed gramps' ID.
|
|
|
|
If no such Source exists, None is returned."""
|
|
|
|
|
2004-08-11 21:42:08 +05:30
|
|
|
data = self.source_map.get(str(val))
|
2004-08-11 09:12:38 +05:30
|
|
|
if data:
|
|
|
|
source = Source()
|
|
|
|
source.unserialize(data)
|
|
|
|
return source
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_object_from_handle(self,handle):
|
|
|
|
"""finds an Object in the database from the passed gramps' ID.
|
|
|
|
If no such Object exists, None is returned."""
|
2004-08-11 21:42:08 +05:30
|
|
|
data = self.media_map.get(str(handle))
|
2004-08-11 09:12:38 +05:30
|
|
|
if data:
|
|
|
|
mobject = MediaObject()
|
|
|
|
mobject.unserialize(data)
|
|
|
|
return mobject
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_place_from_handle(self,handle):
|
|
|
|
"""finds a Place in the database from the passed gramps' ID.
|
|
|
|
If no such Place exists, None is returned."""
|
2004-08-11 21:42:08 +05:30
|
|
|
data = self.place_map.get(str(handle))
|
2004-08-11 09:12:38 +05:30
|
|
|
if data:
|
|
|
|
place = Place()
|
|
|
|
place.unserialize(data)
|
|
|
|
return place
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_event_from_handle(self,handle):
|
|
|
|
"""finds a Place in the database from the passed gramps' ID.
|
|
|
|
If no such Place exists, None is returned."""
|
|
|
|
|
2004-08-11 21:42:08 +05:30
|
|
|
data = self.event_map.get(str(handle))
|
2004-08-11 09:12:38 +05:30
|
|
|
if data:
|
|
|
|
event = Event()
|
|
|
|
event.unserialize(data)
|
|
|
|
return event
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_family_from_handle(self,handle):
|
|
|
|
"""finds a Place in the database from the passed gramps' ID.
|
|
|
|
If no such Place exists, None is returned."""
|
|
|
|
|
|
|
|
data = self.family_map.get(str(handle))
|
|
|
|
if data:
|
|
|
|
family = Family()
|
|
|
|
family.unserialize(data)
|
|
|
|
return family
|
|
|
|
return None
|
|
|
|
|
|
|
|
def find_person_from_handle(self,val,trans):
|
|
|
|
"""finds a Person in the database from the passed gramps' ID.
|
|
|
|
If no such Person exists, a new Person is added to the database."""
|
|
|
|
|
|
|
|
person = Person()
|
2004-08-11 21:42:08 +05:30
|
|
|
data = self.person_map.get(str(val))
|
2004-08-11 09:12:38 +05:30
|
|
|
if data:
|
|
|
|
person.unserialize(data)
|
|
|
|
else:
|
|
|
|
person.set_handle(val)
|
|
|
|
if trans != None:
|
|
|
|
trans.add(PERSON_KEY, val, None)
|
|
|
|
self.person_map[val] = person.serialize()
|
|
|
|
self.genderStats.count_person (person, self)
|
|
|
|
return person
|
|
|
|
|
|
|
|
def find_source_from_handle(self,val,trans):
|
|
|
|
"""finds a Source in the database from the passed gramps' ID.
|
|
|
|
If no such Source exists, a new Source is added to the database."""
|
|
|
|
|
|
|
|
source = Source()
|
|
|
|
if self.source_map.get(str(val)):
|
|
|
|
source.unserialize(self.source_map.get(str(val)))
|
|
|
|
else:
|
|
|
|
source.set_handle(val)
|
|
|
|
if trans != None:
|
|
|
|
trans.add(SOURCE_KEY,val,None)
|
|
|
|
self.source_map[str(val)] = source.serialize()
|
|
|
|
self.smap_index = self.smap_index + 1
|
|
|
|
return source
|
|
|
|
|
|
|
|
def find_event_from_handle(self,val):
|
|
|
|
"""finds a Family in the database from the passed gramps' ID.
|
|
|
|
If no such Family exists, a new Family is added to the database."""
|
|
|
|
data = self.event_map.get(str(val))
|
|
|
|
if data:
|
|
|
|
event = Event()
|
|
|
|
event.unserialize(data)
|
|
|
|
return event
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def find_object_from_handle(self,handle,trans):
|
|
|
|
"""finds an Object in the database from the passed gramps' ID.
|
|
|
|
If no such Object exists, a new Object is added to the database."""
|
|
|
|
|
|
|
|
obj = MediaObject()
|
|
|
|
if self.media_map.get(str(handle)):
|
|
|
|
obj.unserialize(self.media_map.get(str(handle)))
|
|
|
|
else:
|
|
|
|
obj.set_handle(handle)
|
|
|
|
self.add_object(obj,trans)
|
|
|
|
return obj
|
|
|
|
|
|
|
|
def find_place_from_handle(self,handle,trans):
|
|
|
|
"""finds a Place in the database from the passed gramps' ID.
|
|
|
|
If no such Place exists, a new Place is added to the database."""
|
|
|
|
|
|
|
|
data = self.place_map.get(str(handle))
|
|
|
|
place = Place()
|
|
|
|
if not data:
|
|
|
|
place.handle = handle
|
|
|
|
if trans != None:
|
|
|
|
trans.add(PLACE_KEY,handle,None)
|
|
|
|
self.place_map[str(handle)] = place.serialize()
|
|
|
|
self.lmap_index = self.lmap_index + 1
|
|
|
|
else:
|
|
|
|
place.unserialize(data)
|
|
|
|
return place
|
|
|
|
|
|
|
|
def find_family_from_handle(self,val,trans):
|
|
|
|
"""finds a Family in the database from the passed gramps' ID.
|
|
|
|
If no such Family exists, a new Family is added to the database."""
|
|
|
|
|
|
|
|
family = Family()
|
|
|
|
data = self.family_map.get(str(val))
|
|
|
|
if data:
|
|
|
|
family.unserialize(data)
|
|
|
|
else:
|
|
|
|
family.handle = val
|
|
|
|
if trans:
|
|
|
|
trans.add(FAMILY_KEY,val,None)
|
|
|
|
self.family_map[str(val)] = family.serialize()
|
|
|
|
self.fmap_index = self.fmap_index + 1
|
|
|
|
return family
|
|
|
|
|
|
|
|
def add_person(self,person,trans):
|
|
|
|
"""adds a Person to the database, assigning a gramps' ID"""
|
|
|
|
if person.get_gramps_id() == "":
|
|
|
|
person.set_gramps_id(self.find_next_gramps_id())
|
|
|
|
if person.get_handle() == "":
|
|
|
|
person.set_handle(Utils.create_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(PERSON_KEY, person.get_handle(),None)
|
|
|
|
self.person_map[person.get_handle()] = person.serialize()
|
|
|
|
self.genderStats.count_person (person, self)
|
|
|
|
return person.get_handle()
|
|
|
|
|
|
|
|
def add_family(self,family,trans):
|
|
|
|
"""adds a Person to the database, assigning a gramps' ID"""
|
|
|
|
if family.get_gramps_id() == "":
|
|
|
|
family.set_gramps_id(self.find_next_family_gramps_id())
|
|
|
|
if family.get_handle() == "":
|
|
|
|
family.set_handle(Utils.create_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(FAMILY_KEY, family.get_handle(),None)
|
|
|
|
self.family_map[str(family.get_handle())] = family.serialize()
|
|
|
|
return family.get_handle()
|
|
|
|
|
|
|
|
def add_source(self,source,trans):
|
|
|
|
"""adds a Source instance to the database, assigning it a gramps'
|
|
|
|
ID number"""
|
|
|
|
if source.get_handle() == "":
|
|
|
|
source.set_handle(Utils.create_id())
|
|
|
|
if source.get_gramps_id() == "":
|
|
|
|
source.set_gramps_id(self.find_next_source_gramps_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(SOURCE_KEY,source.get_handle(),None)
|
|
|
|
self.source_map[str(source.get_handle())] = source.serialize()
|
|
|
|
return source.get_handle()
|
|
|
|
|
|
|
|
def add_event(self,event,trans):
|
|
|
|
"""adds a Event instance to the database, assigning it a gramps'
|
|
|
|
ID number"""
|
|
|
|
if event.get_handle() == "":
|
|
|
|
event.set_handle(Utils.create_id())
|
|
|
|
if event.get_gramps_id() == "":
|
|
|
|
event.set_gramps_id(self.find_next_event_gramps_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(EVENT_KEY,event.get_handle(),None)
|
|
|
|
self.event_map[str(event.get_handle())] = event.serialize()
|
|
|
|
return event.get_handle()
|
|
|
|
|
|
|
|
def add_place(self,place,trans):
|
|
|
|
"""adds a Place instance to the database, assigning it a gramps'
|
|
|
|
ID number"""
|
|
|
|
|
|
|
|
if place.get_handle() == "":
|
|
|
|
index = Utils.create_id()
|
|
|
|
place.set_handle(index)
|
|
|
|
if place.get_gramps_id() == "":
|
|
|
|
place.set_gramps_id(self.find_next_place_gramps_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(PLACE_KEY,place.get_handle(),None)
|
|
|
|
self.place_map[place.get_handle()] = place.serialize()
|
|
|
|
return place.get_handle()
|
|
|
|
|
|
|
|
def add_object(self,obj,trans):
|
|
|
|
"""adds an Object instance to the database, assigning it a gramps'
|
|
|
|
ID number"""
|
|
|
|
|
|
|
|
index = obj.get_handle()
|
|
|
|
if index == "":
|
|
|
|
index = Utils.create_id()
|
|
|
|
obj.set_handle(index)
|
|
|
|
if obj.get_gramps_id() == "":
|
|
|
|
obj.set_gramps_id(self.find_next_object_gramps_id())
|
|
|
|
if trans != None:
|
|
|
|
trans.add(MEDIA_KEY,index,None)
|
|
|
|
self.media_map[str(index)] = obj.serialize()
|
|
|
|
self.added_files.append(obj)
|
|
|
|
return index
|
|
|
|
|
2004-08-01 09:51:31 +05:30
|
|
|
def get_people_view_maps(self):
|
|
|
|
if self.metadata:
|
|
|
|
return (self.metadata.get('tp_iter'),
|
|
|
|
self.metadata.get('tp_path'),
|
|
|
|
self.metadata.get('p_iter'),
|
|
|
|
self.metadata.get('p_path'),
|
|
|
|
self.metadata.get('sname'))
|
|
|
|
else:
|
|
|
|
return (None,None,None,None,None)
|
|
|
|
|
|
|
|
def set_people_view_maps(self,maps):
|
|
|
|
if self.metadata:
|
|
|
|
self.metadata['tp_iter'] = maps[0]
|
|
|
|
self.metadata['tp_path'] = maps[1]
|
|
|
|
self.metadata['p_iter'] = maps[2]
|
|
|
|
self.metadata['p_path'] = maps[3]
|
|
|
|
self.metadata['sname'] = maps[4]
|
|
|
|
|
|
|
|
def get_added_media_objects(self):
|
|
|
|
return self.added_files
|
|
|
|
|
|
|
|
def clear_added_media_objects(self):
|
|
|
|
self.added_files = []
|
|
|
|
|
|
|
|
def get_number_of_people(self):
|
|
|
|
return len(self.person_map)
|
|
|
|
|
|
|
|
def get_person_keys(self):
|
|
|
|
if self.person_map:
|
|
|
|
return self.person_map.keys()
|
2004-08-11 09:12:38 +05:30
|
|
|
return []
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
def get_family_keys(self):
|
|
|
|
if self.family_map:
|
|
|
|
return self.family_map.keys()
|
2004-08-11 09:12:38 +05:30
|
|
|
return []
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
def sort_by_name(self,f,s):
|
|
|
|
n1 = self.person_map.get(f)[2].sname
|
|
|
|
n2 = self.person_map.get(s)[2].sname
|
|
|
|
return cmp(n1,n2)
|
|
|
|
|
|
|
|
def sort_person_keys(self):
|
|
|
|
if self.person_map:
|
|
|
|
keys = self.person_map.keys()
|
|
|
|
keys.sort(self.sort_by_name)
|
|
|
|
return keys
|
2004-08-11 09:12:38 +05:30
|
|
|
return []
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
def get_person_display(self,key):
|
|
|
|
data = self.person_map.get(key)
|
|
|
|
|
|
|
|
if data[2] == Person.male:
|
|
|
|
gender = const.male
|
|
|
|
elif data[2] == Person.female:
|
|
|
|
gender = const.female
|
|
|
|
else:
|
|
|
|
gender = const.unknown
|
|
|
|
|
|
|
|
return [ data[3].get_name(),
|
|
|
|
data[1],
|
|
|
|
gender,
|
|
|
|
data[7],
|
|
|
|
data[6],
|
|
|
|
data[3].get_sort_name(),
|
|
|
|
data[7],
|
|
|
|
data[6],
|
|
|
|
GrampsCfg.get_display_surname()(data[3])]
|
|
|
|
|
|
|
|
def set_iprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.iprefix = val
|
|
|
|
else:
|
|
|
|
self.iprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.iprefix = "I%04d"
|
|
|
|
|
|
|
|
def set_sprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.sprefix = val
|
|
|
|
else:
|
|
|
|
self.sprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.sprefix = "S%04d"
|
|
|
|
|
|
|
|
def set_oprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.oprefix = val
|
|
|
|
else:
|
|
|
|
self.oprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.oprefix = "O%04d"
|
|
|
|
|
|
|
|
def set_pprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.pprefix = val
|
|
|
|
else:
|
|
|
|
self.pprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.pprefix = "P%04d"
|
|
|
|
|
|
|
|
def set_fprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.fprefix = val
|
|
|
|
else:
|
|
|
|
self.fprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.fprefix = "F%04d"
|
|
|
|
|
|
|
|
def set_eprefix(self,val):
|
|
|
|
if val:
|
|
|
|
if _id_reg.search(val):
|
|
|
|
self.eprefix = val
|
|
|
|
else:
|
|
|
|
self.eprefix = val + "%d"
|
|
|
|
else:
|
|
|
|
self.eprefix = "E%04d"
|
|
|
|
|
|
|
|
def start_transaction(self,msg=""):
|
|
|
|
return Transaction(msg,self.undodb)
|
|
|
|
|
|
|
|
def add_transaction(self,transaction,msg):
|
|
|
|
if not len(transaction):
|
|
|
|
return
|
|
|
|
transaction.set_description(msg)
|
|
|
|
self.undoindex += 1
|
|
|
|
if self.undoindex == _UNDO_SIZE:
|
|
|
|
self.translist = transaction[0:-1] + [ transaction ]
|
|
|
|
else:
|
|
|
|
self.translist[self.undoindex] = transaction
|
|
|
|
|
|
|
|
if self.undolabel:
|
|
|
|
self.undolabel.set_sensitive(1)
|
|
|
|
label = self.undolabel.get_children()[0]
|
|
|
|
label.set_text(_("_Undo %s") % transaction.get_description())
|
|
|
|
label.set_use_underline(1)
|
|
|
|
|
|
|
|
def undo(self):
|
|
|
|
if self.undoindex == -1:
|
|
|
|
return
|
|
|
|
transaction = self.translist[self.undoindex]
|
|
|
|
|
|
|
|
self.undoindex -= 1
|
|
|
|
subitems = transaction.get_recnos()
|
|
|
|
subitems.reverse()
|
|
|
|
for record_id in subitems:
|
|
|
|
(key, handle, data) = transaction.get_record(record_id)
|
|
|
|
if key == PERSON_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.person_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.person_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
elif key == FAMILY_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.family_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.family_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
elif key == SOURCE_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.source_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.source_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
elif key == EVENT_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.event_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.event_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
elif key == PLACE_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.place_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.place_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
elif key == MEDIA_KEY:
|
|
|
|
if data == None:
|
2004-08-11 09:12:38 +05:30
|
|
|
del self.media_map[str(handle)]
|
2004-08-01 09:51:31 +05:30
|
|
|
else:
|
2004-08-11 09:12:38 +05:30
|
|
|
self.media_map[str(handle)] = data
|
2004-08-01 09:51:31 +05:30
|
|
|
|
|
|
|
if self.undolabel:
|
|
|
|
label = self.undolabel.get_children()[0]
|
|
|
|
if self.undoindex == -1:
|
|
|
|
label.set_text(_("_Undo"))
|
|
|
|
self.undolabel.set_sensitive(0)
|
|
|
|
else:
|
|
|
|
transaction = self.translist[self.undoindex]
|
|
|
|
label.set_text(_("_Undo %s") % transaction.get_description())
|
|
|
|
self.undolabel.set_sensitive(1)
|
|
|
|
label.set_use_underline(1)
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
def set_undo_label(self,label):
|
|
|
|
self.undolabel = label
|
|
|
|
self.undolabel.set_sensitive(0)
|
|
|
|
|
|
|
|
def set_redo_label(self,label):
|
|
|
|
self.redolabel = label
|
|
|
|
self.redolabel.set_sensitive(0)
|
|
|
|
|
2004-08-01 09:51:31 +05:30
|
|
|
def get_surnames(self):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def get_eventnames(self):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def get_bookmarks(self):
|
|
|
|
"""returns the list of Person instances in the bookmarks"""
|
|
|
|
return self.bookmarks
|
|
|
|
|
|
|
|
def clean_bookmarks(self):
|
|
|
|
"""cleans up the bookmark list, removing empty slots"""
|
|
|
|
new_bookmarks = []
|
|
|
|
for person_handle in self.bookmarks:
|
|
|
|
new_bookmarks.append(person_handle)
|
|
|
|
self.bookmarks = new_bookmarks
|
|
|
|
|
|
|
|
def set_researcher(self,owner):
|
|
|
|
"""sets the information about the owner of the database"""
|
|
|
|
self.owner.set(owner.get_name(),owner.get_address(),owner.get_city(),
|
|
|
|
owner.get_state(),owner.get_country(),
|
|
|
|
owner.get_postal_code(),owner.get_phone(),owner.get_email())
|
|
|
|
|
|
|
|
def get_researcher(self):
|
|
|
|
"""returns the Researcher instance, providing information about
|
|
|
|
the owner of the database"""
|
|
|
|
return self.owner
|
|
|
|
|
|
|
|
def set_default_person(self,person):
|
|
|
|
"""sets the default Person to the passed instance"""
|
|
|
|
# if (self.default):
|
|
|
|
# self.default.set_ancestor(0)
|
|
|
|
self.metadata['default'] = person.get_handle()
|
|
|
|
# if person:
|
|
|
|
# self.default.set_ancestor(1)
|
|
|
|
|
|
|
|
def set_default_person_handle(self,handle):
|
|
|
|
"""sets the default Person to the passed instance"""
|
|
|
|
self.metadata['default'] = handle
|
|
|
|
|
|
|
|
def get_default_person(self):
|
|
|
|
"""returns the default Person of the database"""
|
|
|
|
if self.metadata and self.metadata.has_key('default'):
|
|
|
|
person = Person()
|
|
|
|
handle = self.metadata['default']
|
2004-08-11 09:12:38 +05:30
|
|
|
data = self.person_map.get(str(handle))
|
2004-08-01 09:51:31 +05:30
|
|
|
person.unserialize(data)
|
|
|
|
return person
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_person(self,handle):
|
|
|
|
"""returns a Person from a GRAMPS's ID"""
|
|
|
|
p = Person()
|
2004-08-11 09:12:38 +05:30
|
|
|
data = self.person_map.get(str(handle))
|
2004-08-01 09:51:31 +05:30
|
|
|
p.unserialize(data)
|
|
|
|
return p
|
|
|
|
|
|
|
|
def get_place_handle_map(self):
|
|
|
|
"""returns a map of gramps's IDs to Place instances"""
|
|
|
|
return self.place_map
|
|
|
|
|
|
|
|
def set_place_handle_map(self,map):
|
|
|
|
"""sets the map of gramps's IDs to Place instances"""
|
|
|
|
self.place_map = map
|
|
|
|
|
|
|
|
def get_family_handle(self,handle):
|
|
|
|
"""returns a map of gramps's IDs to Family instances"""
|
|
|
|
return self.family_map.get(str(handle))
|
|
|
|
|
|
|
|
def get_save_path(self):
|
|
|
|
"""returns the save path of the file, or "" if one does not exist"""
|
|
|
|
return self.path
|
|
|
|
|
|
|
|
def set_save_path(self,path):
|
|
|
|
"""sets the save path for the database"""
|
|
|
|
self.path = path
|
|
|
|
|
|
|
|
def get_person_event_types(self):
|
|
|
|
"""returns a list of all Event types assocated with Person
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_person_attribute_types(self):
|
|
|
|
"""returns a list of all Attribute types assocated with Person
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_family_attribute_types(self):
|
|
|
|
"""returns a list of all Attribute types assocated with Family
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_family_event_types(self):
|
|
|
|
"""returns a list of all Event types assocated with Family
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_media_attribute_types(self):
|
|
|
|
"""returns a list of all Attribute types assocated with Media
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_place_handles(self):
|
|
|
|
"""returns a list of Place instances"""
|
|
|
|
return self.place_map.keys()
|
|
|
|
|
|
|
|
def get_family_relation_types(self):
|
|
|
|
"""returns a list of all relationship types assocated with Family
|
|
|
|
instances in the database"""
|
|
|
|
return []
|
|
|
|
|
|
|
|
def remove_person_handle(self,handle,transaction):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def remove_source_handle(self,handle,transaction):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
def remove_event_handle(self,handle,transaction):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def has_person_handle(self,val):
|
|
|
|
return self.person_map.has_key(str(val))
|
|
|
|
|
|
|
|
def has_family_handle(self,val):
|
|
|
|
return self.family_map.has_key(str(val))
|
2004-08-01 09:51:31 +05:30
|
|
|
|
2004-08-07 10:46:57 +05:30
|
|
|
def get_person_from_gramps_id(self,val):
|
2004-08-01 09:51:31 +05:30
|
|
|
"""finds a Person in the database from the passed gramps' ID.
|
|
|
|
If no such Person exists, a new Person is added to the database."""
|
|
|
|
|
|
|
|
data = self.id_trans.get(str(val))
|
|
|
|
if data:
|
|
|
|
person = Person()
|
|
|
|
person.unserialize(cPickle.loads(data))
|
|
|
|
return person
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def find_person_from_gramps_id(self,val,trans):
|
|
|
|
"""finds a Person in the database from the passed gramps' ID.
|
|
|
|
If no such Person exists, a new Person is added to the database."""
|
|
|
|
|
|
|
|
person = Person()
|
|
|
|
data = self.id_trans.get(str(val))
|
|
|
|
if data:
|
|
|
|
person.unserialize(cPickle.loads(data))
|
|
|
|
else:
|
|
|
|
intid = Utils.create_id()
|
|
|
|
person.set_handle(intid)
|
|
|
|
person.set_gramps_id(val)
|
2004-08-11 09:12:38 +05:30
|
|
|
self.add_person(person,trans)
|
2004-08-01 09:51:31 +05:30
|
|
|
return person
|
|
|
|
|
|
|
|
def has_object_handle(self,handle):
|
|
|
|
"""finds an Object in the database from the passed gramps' ID.
|
|
|
|
If no such Source exists, a new Source is added to the database."""
|
|
|
|
|
|
|
|
return self.media_map.get(str(handle)) != None
|
|
|
|
|
|
|
|
def remove_object(self,handle,transaction):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def remove_place(self,handle,transaction):
|
|
|
|
assert(0,"Needs to be overridden in the derived class")
|
|
|
|
|
|
|
|
def add_place_as(self,place,trans):
|
|
|
|
if trans != None:
|
|
|
|
trans.add(PLACE_KEY,place.get_handle(),None)
|
|
|
|
self.place_map[str(place.get_handle())] = place.serialize()
|
|
|
|
return place.get_handle()
|
|
|
|
|
|
|
|
def sortbyplace(self,f,s):
|
|
|
|
return cmp(self.placesortmap[f], self.placesortmap[s])
|
|
|
|
|
|
|
|
def sortbysource(self,f,s):
|
2004-08-11 09:12:38 +05:30
|
|
|
fp = self.source_map[f][2].upper()
|
|
|
|
sp = self.source_map[s][2].upper()
|
2004-08-01 09:51:31 +05:30
|
|
|
return cmp(fp,sp)
|
|
|
|
|
|
|
|
def sortbymedia(self,f,s):
|
2004-08-11 09:12:38 +05:30
|
|
|
fp = self.media_map[f][4].upper()
|
|
|
|
sp = self.media_map[s][4].upper()
|
2004-08-01 09:51:31 +05:30
|
|
|
return cmp(fp,sp)
|
|
|
|
|
|
|
|
def sort_place_keys(self):
|
|
|
|
if self.place_map:
|
|
|
|
self.placesortmap = {}
|
|
|
|
for key in self.place_map.keys():
|
2004-08-11 09:12:38 +05:30
|
|
|
self.placesortmap[key] = self.place_map[key][2].upper()
|
2004-08-01 09:51:31 +05:30
|
|
|
keys = self.placesortmap.keys()
|
|
|
|
keys.sort(self.sortbyplace)
|
|
|
|
del self.placesortmap
|
|
|
|
return keys
|
|
|
|
return []
|
|
|
|
|
|
|
|
def sort_media_keys(self):
|
|
|
|
if self.media_map:
|
|
|
|
keys = self.media_map.keys()
|
|
|
|
keys.sort(self.sortbymedia)
|
|
|
|
return keys
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def sort_source_keys(self):
|
|
|
|
if self.source_map:
|
|
|
|
keys = self.source_map.keys()
|
|
|
|
keys.sort(self.sortbysource)
|
|
|
|
return keys
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_place_handle_keys(self):
|
|
|
|
if self.place_map:
|
|
|
|
return self.place_map.keys()
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_place_display(self,key):
|
|
|
|
# fix this up better
|
|
|
|
place = Place()
|
|
|
|
place.unserialize(self.place_map[str(key)])
|
|
|
|
return place.get_display_info()
|
|
|
|
|
|
|
|
def get_source_keys(self):
|
|
|
|
if self.source_map:
|
|
|
|
return self.source_map.keys()
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_object_keys(self):
|
|
|
|
if self.media_map:
|
|
|
|
return self.media_map.keys()
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_event_keys(self):
|
|
|
|
if self.event_map:
|
|
|
|
return self.event_map.keys()
|
|
|
|
return []
|
|
|
|
|
|
|
|
def sortbysource(self,f,s):
|
|
|
|
f1 = self.source_map[f][1].upper()
|
|
|
|
s1 = self.source_map[s][1].upper()
|
|
|
|
return cmp(f1,s1)
|
|
|
|
|
|
|
|
def set_source_keys(self):
|
|
|
|
keys = self.source_map.keys()
|
|
|
|
if type(keys) == type([]):
|
|
|
|
keys.sort(self.sortbyplace)
|
|
|
|
return keys
|
|
|
|
|
|
|
|
def get_source_display(self,key):
|
|
|
|
source = Source()
|
|
|
|
source.unserialize(self.source_map.get(str(key)))
|
|
|
|
return source.get_display_info()
|
|
|
|
|
|
|
|
def build_source_display(self,nkey,okey=None):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def new_family(self,trans):
|
|
|
|
"""adds a Family to the database, assigning a gramps' ID"""
|
|
|
|
|
|
|
|
index = self.fprefix % self.fmap_index
|
|
|
|
while self.family_map.get(str(index)):
|
|
|
|
self.fmap_index = self.fmap_index + 1
|
|
|
|
index = self.fprefix % self.fmap_index
|
|
|
|
self.fmap_index = self.fmap_index + 1
|
|
|
|
family = Family()
|
2004-08-11 09:12:38 +05:30
|
|
|
family.set_handle(index)
|
2004-08-01 09:51:31 +05:30
|
|
|
if trans != None:
|
|
|
|
trans.add(FAMILY_KEY, index, None)
|
|
|
|
self.family_map[str(index)] = family.serialize()
|
|
|
|
return family
|
|
|
|
|
|
|
|
def delete_family(self,family_handle,trans):
|
|
|
|
"""deletes the Family instance from the database"""
|
|
|
|
if self.family_map.get(str(family_handle)):
|
|
|
|
if trans != None:
|
|
|
|
old_data = self.family_map.get(str(family_handle))
|
|
|
|
trans.add(FAMILY_KEY,family_handle,old_data)
|
|
|
|
del self.family_map[str(family_handle)]
|
|
|
|
|
|
|
|
def set_column_order(self,list):
|
|
|
|
if self.metadata != None:
|
|
|
|
self.metadata['columns'] = list
|
|
|
|
|
|
|
|
def set_place_column_order(self,list):
|
|
|
|
if self.metadata != None:
|
|
|
|
self.metadata['place_columns'] = list
|
|
|
|
|
|
|
|
def set_source_column_order(self,list):
|
|
|
|
if self.metadata != None:
|
|
|
|
self.metadata['source_columns'] = list
|
|
|
|
|
|
|
|
def set_media_column_order(self,list):
|
|
|
|
if self.metadata != None:
|
|
|
|
self.metadata['media_columns'] = list
|
|
|
|
|
|
|
|
def get_column_order(self):
|
|
|
|
default = [(1,1),(1,2),(1,3),(0,4),(1,5),(0,6),(0,7)]
|
|
|
|
if self.metadata == None:
|
|
|
|
return default
|
|
|
|
else:
|
|
|
|
cols = self.metadata.get('columns',default)
|
|
|
|
if len(cols) != len(default):
|
|
|
|
return cols + default[len(cols):]
|
|
|
|
else:
|
|
|
|
return cols
|
|
|
|
|
|
|
|
def get_place_column_order(self):
|
|
|
|
default = [(1,1),(1,2),(0,3),(1,4),(0,5),(1,6),(0,7),(0,8)]
|
|
|
|
if self.metadata == None:
|
|
|
|
return default
|
|
|
|
else:
|
|
|
|
cols = self.metadata.get('place_columns',default)
|
|
|
|
if len(cols) != len(default):
|
|
|
|
return cols + default[len(cols):]
|
|
|
|
else:
|
|
|
|
return cols
|
|
|
|
|
|
|
|
def get_source_column_order(self):
|
|
|
|
default = [(1,1),(1,2),(1,3),(0,4)]
|
|
|
|
if self.metadata == None:
|
|
|
|
return default
|
|
|
|
else:
|
|
|
|
cols = self.metadata.get('source_columns',default)
|
|
|
|
if len(cols) != len(default):
|
|
|
|
return cols + default[len(cols):]
|
|
|
|
else:
|
|
|
|
return cols
|
|
|
|
|
|
|
|
def get_media_column_order(self):
|
|
|
|
default = [(1,1),(1,2),(1,3)]
|
|
|
|
if self.metadata == None:
|
|
|
|
return default
|
|
|
|
else:
|
|
|
|
cols = self.metadata.get('meda_columns',default)
|
|
|
|
if len(cols) != len(default):
|
|
|
|
return cols + default[len(cols):]
|
|
|
|
else:
|
|
|
|
return cols
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# Transaction
|
|
|
|
#
|
|
|
|
#-------------------------------------------------------------------------
|
|
|
|
class Transaction:
|
|
|
|
|
|
|
|
def __init__(self,msg,db):
|
|
|
|
self.db = db
|
|
|
|
self.first = None
|
|
|
|
self.last = None
|
|
|
|
|
|
|
|
def get_description(self):
|
|
|
|
return self.msg
|
|
|
|
|
|
|
|
def set_description(self,msg):
|
|
|
|
self.msg = msg
|
|
|
|
|
|
|
|
def add(self, type, handle, data):
|
|
|
|
self.last = self.db.append(cPickle.dumps((type,handle,data),1))
|
|
|
|
if self.first == None:
|
|
|
|
self.first = self.last
|
|
|
|
|
|
|
|
def get_recnos(self):
|
|
|
|
return range (self.first, self.last+1)
|
|
|
|
|
|
|
|
def get_record(self,id):
|
|
|
|
return cPickle.loads(self.db[id])
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
if self.last and self.first:
|
|
|
|
return self.last - self.first + 1
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def display(self):
|
|
|
|
for record in self.get_recnos():
|
|
|
|
(key,handle,val) = self.get_record(record)
|
|
|
|
if key == PERSON_KEY:
|
|
|
|
if val:
|
|
|
|
print "PERSON %s change" % handle
|
|
|
|
else:
|
|
|
|
print "PERSON %s remove" % handle
|
|
|
|
elif key == FAMILY_KEY:
|
|
|
|
if val:
|
|
|
|
print "FAMILY %s change" % handle
|
|
|
|
else:
|
|
|
|
print "FAMILY %s remove" % handle
|
|
|
|
elif key == EVENT_KEY:
|
|
|
|
if val:
|
|
|
|
print "EVENT %s change" % handle
|
|
|
|
else:
|
|
|
|
print "EVENT %s remove" % handle
|
|
|
|
elif key == SOURCE_KEY:
|
|
|
|
if val:
|
|
|
|
print "SOURCE %s change" % handle
|
|
|
|
else:
|
|
|
|
print "SOURCE %s remove" % handle
|
|
|
|
elif key == MEDIA_KEY:
|
|
|
|
if val:
|
|
|
|
print "MEDIA %s change" % handle
|
|
|
|
else:
|
|
|
|
print "MEDIA %s remove" % handle
|
|
|
|
elif key == PLACE_KEY:
|
|
|
|
if val:
|
|
|
|
print "PLACE %s change" % handle
|
|
|
|
else:
|
|
|
|
print "PLACE %s remove" % handle
|
|
|
|
|
2004-08-11 09:12:38 +05:30
|
|
|
|