Added Places to CSV import/export

This commit is contained in:
Doug Blank 2015-08-03 20:40:40 -04:00
parent 58d7cbe99d
commit 3df700b22a
2 changed files with 169 additions and 3 deletions

View File

@ -181,13 +181,16 @@ class CSVWriterOptionBox(WriterOptionBox):
""" """
def __init__(self, person, dbstate, uistate): def __init__(self, person, dbstate, uistate):
WriterOptionBox.__init__(self, person, dbstate, uistate) WriterOptionBox.__init__(self, person, dbstate, uistate)
## TODO: add place filter selection
self.include_individuals = 1 self.include_individuals = 1
self.include_marriages = 1 self.include_marriages = 1
self.include_children = 1 self.include_children = 1
self.include_places = 1
self.translate_headers = 1 self.translate_headers = 1
self.include_individuals_check = None self.include_individuals_check = None
self.include_marriages_check = None self.include_marriages_check = None
self.include_children_check = None self.include_children_check = None
self.include_places_check = None
self.translate_headers_check = None self.translate_headers_check = None
def get_option_box(self): def get_option_box(self):
@ -197,16 +200,19 @@ class CSVWriterOptionBox(WriterOptionBox):
self.include_individuals_check = Gtk.CheckButton(label=_("Include people")) self.include_individuals_check = Gtk.CheckButton(label=_("Include people"))
self.include_marriages_check = Gtk.CheckButton(label=_("Include marriages")) self.include_marriages_check = Gtk.CheckButton(label=_("Include marriages"))
self.include_children_check = Gtk.CheckButton(label=_("Include children")) self.include_children_check = Gtk.CheckButton(label=_("Include children"))
self.include_places_check = Gtk.CheckButton(label=_("Include places"))
self.translate_headers_check = Gtk.CheckButton(label=_("Translate headers")) self.translate_headers_check = Gtk.CheckButton(label=_("Translate headers"))
self.include_individuals_check.set_active(1) self.include_individuals_check.set_active(1)
self.include_marriages_check.set_active(1) self.include_marriages_check.set_active(1)
self.include_children_check.set_active(1) self.include_children_check.set_active(1)
self.include_places_check.set_active(1)
self.translate_headers_check.set_active(1) self.translate_headers_check.set_active(1)
option_box.pack_start(self.include_individuals_check, False, True, 0) option_box.pack_start(self.include_individuals_check, False, True, 0)
option_box.pack_start(self.include_marriages_check, False, True, 0) option_box.pack_start(self.include_marriages_check, False, True, 0)
option_box.pack_start(self.include_children_check, False, True, 0) option_box.pack_start(self.include_children_check, False, True, 0)
option_box.pack_start(self.include_places_check, False, True, 0)
option_box.pack_start(self.translate_headers_check, False, True, 0) option_box.pack_start(self.translate_headers_check, False, True, 0)
return option_box return option_box
@ -217,6 +223,7 @@ class CSVWriterOptionBox(WriterOptionBox):
self.include_individuals = self.include_individuals_check.get_active() self.include_individuals = self.include_individuals_check.get_active()
self.include_marriages = self.include_marriages_check.get_active() self.include_marriages = self.include_marriages_check.get_active()
self.include_children = self.include_children_check.get_active() self.include_children = self.include_children_check.get_active()
self.include_places = self.include_places_check.get_active()
self.translate_headers = self.translate_headers_check.get_active() self.translate_headers = self.translate_headers_check.get_active()
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -237,6 +244,7 @@ class CSVWriter(object):
self.plist = {} self.plist = {}
self.flist = {} self.flist = {}
self.place_list = {}
self.persons_details_done = [] self.persons_details_done = []
self.persons_notes_done = [] self.persons_notes_done = []
@ -246,6 +254,7 @@ class CSVWriter(object):
self.include_individuals = 1 self.include_individuals = 1
self.include_marriages = 1 self.include_marriages = 1
self.include_children = 1 self.include_children = 1
self.include_places = 1
self.translate_headers = 1 self.translate_headers = 1
else: else:
self.option_box.parse_options() self.option_box.parse_options()
@ -254,9 +263,24 @@ class CSVWriter(object):
self.include_individuals = self.option_box.include_individuals self.include_individuals = self.option_box.include_individuals
self.include_marriages = self.option_box.include_marriages self.include_marriages = self.option_box.include_marriages
self.include_children = self.option_box.include_children self.include_children = self.option_box.include_children
self.include_places = self.option_box.include_places
self.translate_headers = self.option_box.translate_headers self.translate_headers = self.option_box.translate_headers
self.plist = [x for x in self.db.iter_person_handles()] self.plist = [x for x in self.db.iter_person_handles()]
# make place list so that dependencies are first:
self.place_list = []
place_list = [x for x in self.db.iter_place_handles()]
while place_list:
handle = place_list[0]
place = self.db.get_place_from_handle(handle)
if place:
if all([(x.ref in self.place_list) for x in place.placeref_list]):
self.place_list.append(place_list.pop(0))
else: # put at the back of the line:
place_list.append(place_list.pop(0))
else:
place_list.pop(0)
# get the families for which these people are spouses: # get the families for which these people are spouses:
self.flist = {} self.flist = {}
for key in self.plist: for key in self.plist:
@ -313,9 +337,12 @@ class CSVWriter(object):
self.total += len(self.flist) self.total += len(self.flist)
if self.include_children: if self.include_children:
self.total += len(self.flist) self.total += len(self.flist)
if self.include_places:
self.total += len(self.place_list)
######################## ########################
LOG.debug("Possible people to export: %s", len(self.plist)) LOG.debug("Possible people to export: %s", len(self.plist))
LOG.debug("Possible families to export: %s", len(self.flist)) LOG.debug("Possible families to export: %s", len(self.flist))
LOG.debug("Possible places to export: %s", len(self.place_list))
########################### sort: ########################### sort:
sortorder = [] sortorder = []
dropped_surnames = set() dropped_surnames = set()
@ -530,6 +557,43 @@ class CSVWriter(object):
self.write_csv(family_id, grampsid_ref) self.write_csv(family_id, grampsid_ref)
self.update() self.update()
self.writeln() self.writeln()
###########################
if self.include_places:
if self.translate_headers:
self.write_csv(_("Place"), _("Title"), _("Name"),
_("Type"), _("Latitude"), _("Longitude"),
_("Code"), _("Enclosed_by"), _("Date"))
else:
self.write_csv("Place", "Title", "Name",
"Type", "Latitude", "Longitude",
"Code", "Enclosed_by", "Date")
for key in self.place_list:
place = self.db.get_place_from_handle(key)
if place:
place_id = place.gramps_id
place_title = place.title
place_name = place.name.value
place_type = str(place.place_type)
place_latitude = place.lat
place_longitude = place.long
place_code = place.code
if place.placeref_list:
for placeref in place.placeref_list:
placeref_obj = self.db.get_place_from_handle(placeref.ref)
placeref_date = ""
if not placeref.date.is_empty():
placeref_date = placeref.date
placeref_id = ""
if placeref_obj:
placeref_id = "[%s]" % placeref_obj.gramps_id
self.write_csv("[%s]" % place_id, place_title, place_name, place_type,
place_latitude, place_longitude, place_code, placeref_id,
placeref_date)
else:
self.write_csv("[%s]" % place_id, place_title, place_name, place_type,
place_latitude, place_longitude, place_code, "",
"")
self.writeln()
self.g.close() self.g.close()
return True return True

View File

@ -49,11 +49,15 @@ LOG = logging.getLogger(".ImportCSV")
from gramps.gen.const import GRAMPS_LOCALE as glocale from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext _ = glocale.translation.sgettext
ngettext = glocale.translation.ngettext # else "nearby" comments are ignored ngettext = glocale.translation.ngettext # else "nearby" comments are ignored
from gramps.gen.lib import ChildRef, Citation, Event, EventRef, EventType, Family, FamilyRelType, Name, NameType, Note, NoteType, Person, Place, Source, Surname, Tag from gramps.gen.lib import (ChildRef, Citation, Event, EventRef, EventType,
Family, FamilyRelType, Name, NameType, Note,
NoteType, Person, Place, Source, Surname, Tag,
PlaceName, PlaceType, PlaceRef)
from gramps.gen.db import DbTxn from gramps.gen.db import DbTxn
from gramps.gen.datehandler import parser as _dp from gramps.gen.datehandler import parser as _dp
from gramps.gen.utils.string import gender as gender_map from gramps.gen.utils.string import gender as gender_map
from gramps.gen.utils.id import create_id from gramps.gen.utils.id import create_id
from gramps.gen.utils.location import located_in
from gramps.gen.lib.eventroletype import EventRoleType from gramps.gen.lib.eventroletype import EventRoleType
from gramps.gen.constfunc import conv_to_unicode from gramps.gen.constfunc import conv_to_unicode
from gramps.gen.config import config from gramps.gen.config import config
@ -125,8 +129,19 @@ class CSVParser(object):
self.index = 0 self.index = 0
self.fam_count = 0 self.fam_count = 0
self.indi_count = 0 self.indi_count = 0
self.place_count = 0
self.pref = {} # person ref, internal to this sheet self.pref = {} # person ref, internal to this sheet
self.fref = {} # family ref, internal to this sheet self.fref = {} # family ref, internal to this sheet
self.placeref = {}
self.place_types = {}
# Build reverse dictionary, name to type number
for items in PlaceType().get_map().items(): # (0, 'Custom')
self.place_types[items[1]] = items[0]
if _(items[1]) != items[1]:
self.place_types[_(items[1])] = items[0]
# Add custom types:
for custom_type in self.db.get_place_types():
self.place_types[custom_type] = 0
column2label = { column2label = {
"surname": ("Lastname", "Surname", _("Surname"), "lastname", "surname": ("Lastname", "Surname", _("Surname"), "lastname",
"last_name", "surname", _("surname")), "last_name", "surname", _("surname")),
@ -189,6 +204,14 @@ class CSVParser(object):
"marriage": ("Marriage", _("Marriage"), "marriage", _("marriage")), "marriage": ("Marriage", _("Marriage"), "marriage", _("marriage")),
"date": ("Date", _("Date"), "date", _("date")), "date": ("Date", _("Date"), "date", _("date")),
"place": ("Place", _("Place"), "place", _("place")), "place": ("Place", _("Place"), "place", _("place")),
"title": ("Title", _("Title"), "title", _("title")),
"name": ("Name", _("Name"), "name", _("name")),
"type": ("Type", _("Type"), "type", _("type")),
"latitude": ("Latitude", _("latitude"), "latitude", _("latitude")),
"longitude": ("Longitude", _("Longitude"), "longitude", _("longitude")),
"code": ("Code", _("Code"), "code", _("code")),
"enclosed_by": ("Enclosed by", _("Enclosed by"), "enclosed by", _("enclosed by"),
"enclosed_by", _("enclosed_by"), "Enclosed_by", _("Enclosed_by"))
} }
lab2col_dict = [] lab2col_dict = []
for key in list(column2label.keys()): for key in list(column2label.keys()):
@ -251,6 +274,18 @@ class CSVParser(object):
return self.pref[id_.lower()] return self.pref[id_.lower()]
else: else:
return None return None
elif type_ == "place":
if id_.startswith("[") and id_.endswith("]"):
id_ = self.db.id2user_format(id_[1:-1])
db_lookup = self.db.get_place_from_gramps_id(id_)
if db_lookup is None:
return self.lookup(type_, id_)
else:
return db_lookup
elif id_.lower() in self.placeref:
return self.placeref[id_.lower()]
else:
return None
else: else:
LOG.warn("invalid lookup type in CSV import: '%s'" % type_) LOG.warn("invalid lookup type in CSV import: '%s'" % type_)
return None return None
@ -266,6 +301,9 @@ class CSVParser(object):
elif type_ == "family": elif type_ == "family":
id_ = self.db.fid2user_format(id_) id_ = self.db.fid2user_format(id_)
self.fref[id_.lower()] = object_ self.fref[id_.lower()] = object_
elif type_ == "place":
id_ = self.db.pid2user_format(id_)
self.placeref[id_.lower()] = object_
else: else:
LOG.warn("invalid storeup type in CSV import: '%s'" % type_) LOG.warn("invalid storeup type in CSV import: '%s'" % type_)
@ -305,8 +343,10 @@ class CSVParser(object):
self.index = 0 self.index = 0
self.fam_count = 0 self.fam_count = 0
self.indi_count = 0 self.indi_count = 0
self.place_count = 0
self.pref = {} # person ref, internal to this sheet self.pref = {} # person ref, internal to this sheet
self.fref = {} # family ref, internal to this sheet self.fref = {} # family ref, internal to this sheet
self.placeref = {}
header = None header = None
line_number = 0 line_number = 0
for row in data: for row in data:
@ -324,7 +364,7 @@ class CSVParser(object):
col[key] = count col[key] = count
count += 1 count += 1
continue continue
# three different kinds of data: person, family, and marriage # four different kinds of data: person, family, and marriage
if (("marriage" in header) or if (("marriage" in header) or
("husband" in header) or ("husband" in header) or
("wife" in header)): ("wife" in header)):
@ -333,6 +373,8 @@ class CSVParser(object):
self._parse_family(line_number, row, col) self._parse_family(line_number, row, col)
elif "surname" in header: elif "surname" in header:
self._parse_person(line_number, row, col) self._parse_person(line_number, row, col)
elif "place" in header:
self._parse_place(line_number, row, col)
else: else:
LOG.warn("ignoring line %d" % line_number) LOG.warn("ignoring line %d" % line_number)
return None return None
@ -670,6 +712,56 @@ class CSVParser(object):
self.find_and_set_citation(person, source) self.find_and_set_citation(person, source)
self.db.commit_person(person, self.trans) self.db.commit_person(person, self.trans)
def _parse_place(self, line_number, row, col):
"Parse the content of a Place line."
place_id = rd(line_number, row, col, "place")
place_title = rd(line_number, row, col, "title")
place_name = rd(line_number, row, col, "name")
place_type_str = rd(line_number, row, col, "type")
place_latitude = rd(line_number, row, col, "latitude")
place_longitude = rd(line_number, row, col, "longitude")
place_code = rd(line_number, row, col, "code")
place_enclosed_by_id = rd(line_number, row, col, "enclosed_by")
place_date = rd(line_number, row, col, "date")
#########################################################
# if this place already exists, don't create it
place = self.lookup("place", place_id)
if place is None:
# new place
place = self.create_place()
self.storeup("place", place_id.lower(), place)
if place_title is not None:
place.title = place_title
if place_name is not None:
place.name = PlaceName(value=place_name)
if place_type_str is not None:
place.place_type = self.get_place_type(place_type_str)
if place_latitude is not None:
place.lat = place_latitude
if place_longitude is not None:
place.long = place_longitude
if place_code is not None:
place.code = place_code
if place_enclosed_by_id is not None:
place_enclosed_by = self.lookup("place", place_enclosed_by_id)
if place_enclosed_by is None:
raise Exception("cannot enclose %s in %s as it doesn't exist" % (place.gramps_id, place_enclosed_by_id))
if not place_enclosed_by.handle in place.placeref_list:
placeref = PlaceRef()
placeref.ref = place_enclosed_by.handle
if place_date:
placeref.date = _dp.parse(place_date)
place.placeref_list.append(placeref)
#########################################################
self.db.commit_place(place, self.trans)
def get_place_type(self, place_type_str):
if place_type_str in self.place_types:
return PlaceType((self.place_types[place_type_str], place_type_str))
else:
# New custom type:
return PlaceType((0, place_type_str))
def get_or_create_family(self, family_ref, husband, wife): def get_or_create_family(self, family_ref, husband, wife):
"Return the family object for the give family ID." "Return the family object for the give family ID."
# if a gramps_id and exists: # if a gramps_id and exists:
@ -763,6 +855,15 @@ class CSVParser(object):
self.indi_count += 1 self.indi_count += 1
return person return person
def create_place(self):
""" Used to create a new person we know doesn't exist """
place = Place()
if self.default_tag:
place.add_tag(self.default_tag.handle)
self.db.add_place(place, self.trans)
self.place_count += 1
return place
def get_or_create_place(self, place_name): def get_or_create_place(self, place_name):
"Return the requested place object tuple-packed with a new indicator." "Return the requested place object tuple-packed with a new indicator."
LOG.debug("get_or_create_place: looking for: %s", place_name) LOG.debug("get_or_create_place: looking for: %s", place_name)
@ -773,6 +874,7 @@ class CSVParser(object):
return (0, place) return (0, place)
place = Place() place = Place()
place.set_title(place_name) place.set_title(place_name)
place.name = PlaceName(value=place_name)
self.db.add_place(place, self.trans) self.db.add_place(place, self.trans)
return (1, place) return (1, place)