4114: Would like to use stdin and stdout for command line import/export; Xml import
svn: r16765
This commit is contained in:
parent
6d02067406
commit
b10d52a3bf
@ -84,6 +84,21 @@ class GedcomError(Exception):
|
||||
"Return string representation"
|
||||
return self.value
|
||||
|
||||
class GrampsImportError(Exception):
|
||||
"""Error used to report mistakes during import of files into Gramps"""
|
||||
def __init__(self, value, value2=""):
|
||||
Exception.__init__(self)
|
||||
self.value = value
|
||||
self.value2 = value2
|
||||
|
||||
def __str__(self):
|
||||
"Return string representation"
|
||||
return self.value
|
||||
|
||||
def messages(self):
|
||||
"Return the messages"
|
||||
return (self.value, self.value2)
|
||||
|
||||
class PluginError(Exception):
|
||||
"""Error used to report plugin errors"""
|
||||
def __init__(self, value):
|
||||
|
@ -29,6 +29,7 @@
|
||||
#-------------------------------------------------------------------------
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from xml.parsers.expat import ExpatError, ParserCreate
|
||||
from gen.ggettext import gettext as _
|
||||
import re
|
||||
@ -44,6 +45,7 @@ from QuestionDialog import ErrorDialog, WarningDialog
|
||||
import gen.mime
|
||||
import gen.lib
|
||||
from gen.db import DbTxn
|
||||
from Errors import GrampsImportError
|
||||
import Utils
|
||||
import DateHandler
|
||||
from gen.display.name import displayer as name_displayer
|
||||
@ -99,44 +101,44 @@ def importData(database, filename, callback=None):
|
||||
database.smap = {}
|
||||
database.pmap = {}
|
||||
database.fmap = {}
|
||||
line_cnt = 0
|
||||
person_cnt = 0
|
||||
|
||||
xml_file = open_file(filename)
|
||||
versionparser = VersionParser(xml_file)
|
||||
|
||||
if xml_file is None or \
|
||||
version_is_valid(versionparser) is False:
|
||||
return
|
||||
|
||||
version_string = versionparser.get_xmlns_version()
|
||||
#reset file to the start
|
||||
xml_file.seek(0)
|
||||
with ImportOpenFileContextManager(filename) as xml_file:
|
||||
if xml_file is None:
|
||||
return
|
||||
|
||||
change = os.path.getmtime(filename)
|
||||
parser = GrampsParser(database, callback, change, version_string)
|
||||
if filename == '-':
|
||||
change = time.time()
|
||||
else:
|
||||
change = os.path.getmtime(filename)
|
||||
parser = GrampsParser(database, callback, change)
|
||||
|
||||
linecounter = LineParser(filename)
|
||||
line_cnt = linecounter.get_count()
|
||||
person_cnt = linecounter.get_person_count()
|
||||
if filename != '-':
|
||||
linecounter = LineParser(filename)
|
||||
line_cnt = linecounter.get_count()
|
||||
person_cnt = linecounter.get_person_count()
|
||||
|
||||
read_only = database.readonly
|
||||
database.readonly = False
|
||||
|
||||
try:
|
||||
info = parser.parse(xml_file, line_cnt, person_cnt)
|
||||
except IOError, msg:
|
||||
ErrorDialog(_("Error reading %s") % filename, str(msg))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
except ExpatError, msg:
|
||||
ErrorDialog(_("Error reading %s") % filename,
|
||||
_("The file is probably either corrupt or not a valid Gramps database."))
|
||||
return
|
||||
|
||||
xml_file.close()
|
||||
read_only = database.readonly
|
||||
database.readonly = False
|
||||
|
||||
try:
|
||||
info = parser.parse(xml_file, line_cnt, person_cnt)
|
||||
except GrampsImportError, err: # version error
|
||||
ErrorDialog(*err.messages())
|
||||
return
|
||||
except IOError, msg:
|
||||
ErrorDialog(_("Error reading %s") % filename, str(msg))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
except ExpatError, msg:
|
||||
ErrorDialog(_("Error reading %s") % filename,
|
||||
_("The file is probably either corrupt or not a "
|
||||
"valid Gramps database."))
|
||||
return
|
||||
|
||||
database.readonly = read_only
|
||||
|
||||
return info
|
||||
|
||||
## TODO - WITH MEDIA PATH, IS THIS STILL NEEDED?
|
||||
@ -333,6 +335,64 @@ class LineParser(object):
|
||||
def get_person_count(self):
|
||||
return self.person_count
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# ImportOpenFileContextManager
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class ImportOpenFileContextManager:
|
||||
"""
|
||||
Context manager to open a file or stdin for reading.
|
||||
"""
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.filehandle = None
|
||||
|
||||
def __enter__(self):
|
||||
if self.filename == '-':
|
||||
self.filehandle = sys.stdin
|
||||
else:
|
||||
self.filehandle = self.open_file(self.filename)
|
||||
return self.filehandle
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if self.filename != '-':
|
||||
self.filehandle.close()
|
||||
return False
|
||||
|
||||
def open_file(self, filename):
|
||||
"""
|
||||
Open the xml file.
|
||||
Return a valid file handle if the file opened sucessfully.
|
||||
Return None if the file was not able to be opened.
|
||||
"""
|
||||
if GZIP_OK:
|
||||
use_gzip = True
|
||||
try:
|
||||
ofile = gzip.open(filename, "r")
|
||||
ofile.read(1)
|
||||
ofile.close()
|
||||
except IOError, msg:
|
||||
use_gzip = False
|
||||
except ValueError, msg:
|
||||
use_gzip = True
|
||||
else:
|
||||
use_gzip = False
|
||||
|
||||
try:
|
||||
if use_gzip:
|
||||
xml_file = gzip.open(filename, "rb")
|
||||
else:
|
||||
xml_file = open(filename, "r")
|
||||
except IOError, msg:
|
||||
ErrorDialog(_("%s could not be opened") % filename, str(msg))
|
||||
xml_file = None
|
||||
except:
|
||||
ErrorDialog(_("%s could not be opened") % filename)
|
||||
xml_file = None
|
||||
|
||||
return xml_file
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Gramps database parsing class. Derived from SAX XML parser
|
||||
@ -340,10 +400,10 @@ class LineParser(object):
|
||||
#-------------------------------------------------------------------------
|
||||
class GrampsParser(UpdateCallback):
|
||||
|
||||
def __init__(self, database, callback, change, version_string):
|
||||
def __init__(self, database, callback, change):
|
||||
UpdateCallback.__init__(self, callback)
|
||||
#version of the xml file
|
||||
self.version_string = version_string
|
||||
self.__gramps_version = 'unknown'
|
||||
self.__xml_version = '1.0.0'
|
||||
self.stext_list = []
|
||||
self.scomments_list = []
|
||||
self.note_list = []
|
||||
@ -490,7 +550,7 @@ class GrampsParser(UpdateCallback):
|
||||
"region": (self.start_region, None),
|
||||
"father": (self.start_father, None),
|
||||
"gender": (None, self.stop_gender),
|
||||
"header": (None, None),
|
||||
"header": (None, self.stop_header),
|
||||
"map": (self.start_namemap, None),
|
||||
"mediapath": (None, self.stop_mediapath),
|
||||
"mother": (self.start_mother, None),
|
||||
@ -804,6 +864,93 @@ class GrampsParser(UpdateCallback):
|
||||
self.db.request_rebuild()
|
||||
return self.info
|
||||
|
||||
def start_database(self, attrs):
|
||||
"""
|
||||
Get the xml version of the file.
|
||||
"""
|
||||
if 'xmlns' in attrs:
|
||||
xmlns = attrs.get('xmlns').split('/')
|
||||
if len(xmlns)>= 2 and not xmlns[2] == 'gramps-project.org':
|
||||
self.__xml_version = '0.0.0'
|
||||
else:
|
||||
try:
|
||||
self.__xml_version = xmlns[4]
|
||||
except:
|
||||
#leave version at 1.0.0 although it could be 0.0.0 ??
|
||||
pass
|
||||
else:
|
||||
#1.0 or before xml, no dtd schema yet on
|
||||
# http://www.gramps-project.org/xml/
|
||||
self.__xml_version = '0.0.0'
|
||||
|
||||
def start_created(self, attrs):
|
||||
"""
|
||||
Get the Gramps version that produced the file.
|
||||
"""
|
||||
if 'sources' in attrs:
|
||||
self.num_srcs = int(attrs['sources'])
|
||||
else:
|
||||
self.num_srcs = 0
|
||||
if 'places' in attrs:
|
||||
self.num_places = int(attrs['places'])
|
||||
else:
|
||||
self.num_places = 0
|
||||
if 'version' in attrs:
|
||||
self.__gramps_version = attrs.get('version')
|
||||
|
||||
def stop_header(self, *dummy):
|
||||
"""
|
||||
Check the version of Gramps and XML.
|
||||
"""
|
||||
if self.__gramps_version == 'unknown':
|
||||
msg = _("The .gramps file you are importing does not contain the "
|
||||
"version of Gramps with which it was produced.\n\n"
|
||||
"The file will not be imported.")
|
||||
raise GrampsImportError(_('Import file misses Gramps version'), msg)
|
||||
if not re.match("\d+\.\d+\.\d+", self.__xml_version):
|
||||
msg = _("The .gramps file you are importing does not contain a "
|
||||
"valid xml-namespace number.\n\n"
|
||||
"The file will not be imported.")
|
||||
raise GrampsImportError(_('Import file contains unacceptable XML '
|
||||
'namespace version'), msg)
|
||||
if self.__xml_version > libgrampsxml.GRAMPS_XML_VERSION:
|
||||
msg = _("The .gramps file you are importing was made by "
|
||||
"version %(newer)s of "
|
||||
"Gramps, while you are running an older version %(older)s. "
|
||||
"The file will not be imported. Please upgrade to the "
|
||||
"latest version of Gramps and try again." ) % {
|
||||
'newer' : self.__gramps_version, 'older' : const.VERSION }
|
||||
raise GrampsImportError('', msg)
|
||||
if self.__xml_version < '1.0.0':
|
||||
msg = _("The .gramps file you are importing was made by version "
|
||||
"%(oldgramps)s of Gramps, while you are running a more "
|
||||
"recent version %(newgramps)s.\n\n"
|
||||
"The file will not be imported. Please use an older version"
|
||||
" of Gramps that supports version %(xmlversion)s of the "
|
||||
"xml.\nSee\n "
|
||||
"http://gramps-project.org/wiki/index.php?title=GRAMPS_XML"
|
||||
"\n for more info."
|
||||
) % {'oldgramps': self.__gramps_version,
|
||||
'newgramps': const.VERSION,
|
||||
'xmlversion': self.__xml_version,
|
||||
}
|
||||
raise GrampsImportError(_('The file will not be imported'), msg)
|
||||
elif self.__xml_version < '1.1.0':
|
||||
msg = _("The .gramps file you are importing was made by version "
|
||||
"%(oldgramps)s of Gramps, while you are running a much "
|
||||
"more recent version %(newgramps)s.\n\n"
|
||||
"Ensure after import everything is imported correctly. In "
|
||||
"the event of problems, please submit a bug and use an "
|
||||
"older version of Gramps in the meantime to import this "
|
||||
"file, which is version %(xmlversion)s of the xml.\nSee\n "
|
||||
"http://gramps-project.org/wiki/index.php?title=GRAMPS_XML"
|
||||
"\nfor more info."
|
||||
) % {'oldgramps': self.__gramps_version,
|
||||
'newgramps': const.VERSION,
|
||||
'xmlversion': self.__xml_version,
|
||||
}
|
||||
WarningDialog(_('Old xml file'), msg)
|
||||
|
||||
def start_lds_ord(self, attrs):
|
||||
self.ord = gen.lib.LdsOrd()
|
||||
self.ord.set_type_from_xml(attrs['type'])
|
||||
@ -1293,7 +1440,7 @@ class GrampsParser(UpdateCallback):
|
||||
self.name = gen.lib.Name()
|
||||
name_type = attrs.get('type', "Birth Name")
|
||||
# Mapping "Other Name" from gramps 2.0.x to Unknown
|
||||
if (self.version_string=='1.0.0') and (name_type=='Other Name'):
|
||||
if (self.__xml_version == '1.0.0') and (name_type == 'Other Name'):
|
||||
self.name.set_type(gen.lib.NameType.UNKNOWN)
|
||||
else:
|
||||
self.name.type.set_from_xml_str(name_type)
|
||||
@ -1955,20 +2102,6 @@ class GrampsParser(UpdateCallback):
|
||||
|
||||
date_value.set_as_text(attrs['val'])
|
||||
|
||||
def start_created(self, attrs):
|
||||
if 'sources' in attrs:
|
||||
self.num_srcs = int(attrs['sources'])
|
||||
else:
|
||||
self.num_srcs = 0
|
||||
if 'places' in attrs:
|
||||
self.num_places = int(attrs['places'])
|
||||
else:
|
||||
self.num_places = 0
|
||||
|
||||
def start_database(self, attrs):
|
||||
# we already parsed xml once in VersionParser to obtain version
|
||||
pass
|
||||
|
||||
def start_pos(self, attrs):
|
||||
self.person.position = (int(attrs["x"]), int(attrs["y"]))
|
||||
|
||||
@ -2557,129 +2690,3 @@ def build_place_title(loc):
|
||||
value = append_value(value, loc.country)
|
||||
return value
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# VersionParser
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
class VersionParser(object):
|
||||
"""
|
||||
Utility class to quickly get the versions from an XML file.
|
||||
"""
|
||||
def __init__(self, xml_file):
|
||||
"""
|
||||
xml_file must be a file object that is already open.
|
||||
"""
|
||||
self.__p = ParserCreate()
|
||||
self.__p.StartElementHandler = self.__element_handler
|
||||
self.__gramps_version = 'unknown'
|
||||
self.__xml_version = '1.0.0'
|
||||
xml_file.seek(0)
|
||||
self.__p.ParseFile(xml_file)
|
||||
|
||||
def __element_handler(self, tag, attrs):
|
||||
" Handle XML elements "
|
||||
if tag == "database" and 'xmlns' in attrs:
|
||||
xmlns = attrs.get('xmlns').split('/')
|
||||
if len(xmlns)>= 2 and not xmlns[2] == 'gramps-project.org':
|
||||
self.__xml_version = '0.0.0'
|
||||
else:
|
||||
try:
|
||||
self.__xml_version = xmlns[4]
|
||||
except:
|
||||
#leave version at 1.0.0 although it could be 0.0.0 ??
|
||||
pass
|
||||
elif tag == "database" and not 'xmlns' in attrs:
|
||||
#1.0 or before xml, no dtd schema yet on
|
||||
# http://www.gramps-project.org/xml/
|
||||
self.__xml_version = '0.0.0'
|
||||
|
||||
elif tag == "created" and 'version' in attrs:
|
||||
self.__gramps_version = attrs.get('version')
|
||||
|
||||
def get_xmlns_version(self):
|
||||
" Get the namespace version of the file "
|
||||
return self.__xml_version
|
||||
|
||||
def get_gramps_version(self):
|
||||
" Get the version of Gramps that created the file "
|
||||
return self.__gramps_version
|
||||
|
||||
def open_file(filename):
|
||||
"""
|
||||
Open the xml file.
|
||||
Return a valid file handle if the file opened sucessfully.
|
||||
Return None if the file was not able to be opened.
|
||||
"""
|
||||
if GZIP_OK:
|
||||
use_gzip = True
|
||||
try:
|
||||
ofile = gzip.open(filename, "r")
|
||||
ofile.read(1)
|
||||
ofile.close()
|
||||
except IOError, msg:
|
||||
use_gzip = False
|
||||
except ValueError, msg:
|
||||
use_gzip = True
|
||||
else:
|
||||
use_gzip = False
|
||||
|
||||
try:
|
||||
if use_gzip:
|
||||
xml_file = gzip.open(filename, "rb")
|
||||
else:
|
||||
xml_file = open(filename, "r")
|
||||
except IOError, msg:
|
||||
ErrorDialog(_("%s could not be opened") % filename, str(msg))
|
||||
xml_file = None
|
||||
except:
|
||||
ErrorDialog(_("%s could not be opened") % filename)
|
||||
xml_file = None
|
||||
|
||||
return xml_file
|
||||
|
||||
def version_is_valid(versionparser):
|
||||
"""
|
||||
Validate the xml version.
|
||||
:param versionparser: A VersionParser object to work with
|
||||
"""
|
||||
|
||||
if versionparser.get_xmlns_version() > libgrampsxml.GRAMPS_XML_VERSION:
|
||||
msg = _("The .gramps file you are importing was made by version %(newer)s of "
|
||||
"Gramps, while you are running an older version %(older)s. "
|
||||
"The file will not be imported. Please upgrade to the latest "
|
||||
"version of Gramps and try again." ) % {
|
||||
'newer' : versionparser.get_gramps_version(), 'older' : const.VERSION }
|
||||
ErrorDialog(msg)
|
||||
return False
|
||||
if versionparser.get_xmlns_version() < '1.0.0':
|
||||
msg = _("The .gramps file you are importing was made by version "
|
||||
"%(oldgramps)s of Gramps, while you are running a more "
|
||||
"recent version %(newgramps)s.\n\n"
|
||||
"The file will not be imported. Please use an older version of"
|
||||
" Gramps that supports version %(xmlversion)s of the xml.\nSee"
|
||||
"\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n "
|
||||
"for more info."
|
||||
) % {'oldgramps': versionparser.get_gramps_version(),
|
||||
'newgramps': const.VERSION,
|
||||
'xmlversion': versionparser.get_xmlns_version(),
|
||||
}
|
||||
ErrorDialog(_('The file will not be imported'), msg)
|
||||
return False
|
||||
elif versionparser.get_xmlns_version() < '1.1.0':
|
||||
msg = _("The .gramps file you are importing was made by version "
|
||||
"%(oldgramps)s of Gramps, while you are running a much "
|
||||
"more recent version %(newgramps)s.\n\n"
|
||||
"Ensure after import everything is imported correctly. In the "
|
||||
"event of problems, please submit a bug and use an older "
|
||||
"version of Gramps in the meantime to import this file, which "
|
||||
"is version %(xmlversion)s of the xml.\nSee"
|
||||
"\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n"
|
||||
"for more info."
|
||||
) % {'oldgramps': versionparser.get_gramps_version(),
|
||||
'newgramps': const.VERSION,
|
||||
'xmlversion': versionparser.get_xmlns_version(),
|
||||
}
|
||||
WarningDialog(_('Old xml file'), msg)
|
||||
return True
|
||||
return True
|
||||
|
Loading…
Reference in New Issue
Block a user