diff --git a/pyexcel_odsr/__init__.py b/pyexcel_odsr/__init__.py index 959c839..b779b62 100644 --- a/pyexcel_odsr/__init__.py +++ b/pyexcel_odsr/__init__.py @@ -18,6 +18,12 @@ stream_type='binary' ) +__FILE_TYPE_FODS__ = 'fods' +IOPluginInfoChain(__name__).add_a_reader( + relative_plugin_class_path='odsr.FODSBook', + file_types=[__FILE_TYPE_FODS__], + stream_type='text' +) from pyexcel_io.io import get_data as read_data, isstream diff --git a/pyexcel_odsr/messyods.py b/pyexcel_odsr/messyods.py index 6540db1..7da348a 100644 --- a/pyexcel_odsr/messyods.py +++ b/pyexcel_odsr/messyods.py @@ -34,6 +34,13 @@ ODS_TABLE_NAME = re.compile(b".*?table:name=\"(.*?)\".*?") ODS_ROW_MATCH = re.compile(b".*?().*?", re.MULTILINE) +FODS_NAMESPACES_TAG_MATCH = re.compile(b"(]*>)", + re.DOTALL) +FODS_TABLE_MATCH = re.compile(b".*?().*?", + re.DOTALL) +FODS_TABLE_NAME = re.compile(b".*?table:name=\"(.*?)\".*?") +FODS_ROW_MATCH = re.compile(b".*?().*?", + re.DOTALL) NS_OPENDOCUMENT_PTTN = u"urn:oasis:names:tc:opendocument:xmlns:%s" NS_CAL_PTTN = u"urn:org:documentfoundation:names:experimental:calc:xmlns:%s" NS_OPENDOCUMENT_TABLE = NS_OPENDOCUMENT_PTTN % "table:1.0" @@ -172,6 +179,132 @@ def raw(self, sample=False): del rows +class FODSTableSet(object): + """ + A wrapper around ODS files. Because they are zipped and the info we want + is in the zipped file as content.xml we must ensure that we either have + a seekable object (local file) or that we retrieve all of the content from + the remote URL. + """ + + def __init__(self, fileobj, window=None, **kw): + '''Initialize the object. + + :param fileobj: may be a file path or a file-like object. Note the + file-like object *must* be in binary mode and must be seekable (it will + get passed to zipfile). + + As a specific tip: urllib2.urlopen returns a file-like object that is + not in file-like mode while urllib.urlopen *does*! + + To get a seekable file you *cannot* use + messytables.core.seekable_stream as it does not support the full seek + functionality. + ''' + if hasattr(fileobj, 'read'): + # wrap in a StringIO so we do not have hassle with seeks and + # binary etc (see notes to __init__ above) + # TODO: rather wasteful if in fact fileobj comes from disk + fileobj = io.BytesIO(fileobj.read()) + + self.window = window + + self.content = open(fileobj, 'rb').read() + + def make_tables(self): + """ + Return the sheets in the workbook. + + A regex is used for this to avoid having to: + + 1. load large the entire file into memory, or + 2. SAX parse the file more than once + """ + namespace_tags = self._get_namespace_tags() + sheets = [m.groups(0)[0] + for m in FODS_TABLE_MATCH.finditer(self.content)] + return [FODSRowSet(sheet, self.window, namespace_tags) + for sheet in sheets] + + def _get_namespace_tags(self): + match = re.search(FODS_NAMESPACES_TAG_MATCH, self.content) + assert match + tag_open = match.groups()[0] + tag_close = b'' + return tag_open, tag_close + + +class FODSRowSet(object): + """ ODS support for a single sheet in the ODS workbook. Unlike + the CSV row set this is not a streaming operation. """ + + def __init__(self, sheet, window=None, namespace_tags=None): + self.sheet = sheet + + self.name = "Unknown" + m = FODS_TABLE_NAME.match(self.sheet) + if m: + self.name = m.groups(0)[0] + if not PY2 and isinstance(self.name, bytes): + self.name = self.name.decode('utf-8') + + self.window = window or 1000 + + # We must wrap the XML fragments in a valid header otherwise iterparse + # will explode with certain (undefined) versions of libxml2. The + # namespaces are in the ODS file, and change with the libreoffice + # version saving it, so get them from the ODS file if possible. The + # default namespaces are an option to preserve backwards compatibility + # of ODSRowSet. + if namespace_tags: + self.namespace_tags = namespace_tags + else: + namespaces = { + "dc": u"http://purl.org/dc/elements/1.1/", + "draw": NS_OPENDOCUMENT_PTTN % u"drawing:1.0", + "number": NS_OPENDOCUMENT_PTTN % u"datastyle:1.0", + "office": NS_OPENDOCUMENT_PTTN % u"office:1.0", + "svg": NS_OPENDOCUMENT_PTTN % u"svg-compatible:1.0", + "table": NS_OPENDOCUMENT_PTTN % u"table:1.0", + "text": NS_OPENDOCUMENT_PTTN % u"text:1.0", + "calcext": NS_CAL_PTTN % u"calcext:1.0", + } + + ods_header = u""\ + .format(" ".join('xmlns:{0}="{1}"'.format(k, v) + for k, v in namespaces.iteritems())).encode('utf-8') + ods_footer = u"".encode('utf-8') + self.namespace_tags = (ods_header, ods_footer) + + def raw(self, sample=False): + """ Iterate over all rows in this sheet. """ + rows = FODS_ROW_MATCH.findall(self.sheet) + + for row in rows: + row_data = [] + + block = self.namespace_tags[0] + row + self.namespace_tags[1] + partial = io.BytesIO(block) + + for action, element in etree.iterparse(partial, ('end',)): + if element.tag != _tag(NS_OPENDOCUMENT_TABLE, TABLE_CELL): + continue + + cell = _read_cell(element) + repeat = element.attrib.get( + _tag(NS_OPENDOCUMENT_TABLE, COLUMN_REPEAT)) + + if repeat: + number_of_repeat = int(repeat) + row_data += [cell] * number_of_repeat + else: + row_data.append(cell) + + del partial + yield row_data + del rows + + def _read_cell(element): cell_type = element.attrib.get(_tag(NS_OPENDOCUMENT_OFFICE, VALUE_TYPE)) value_token = VALUE_TOKEN.get(cell_type, 'value') diff --git a/pyexcel_odsr/odsr.py b/pyexcel_odsr/odsr.py index e728c0e..6b74fd7 100644 --- a/pyexcel_odsr/odsr.py +++ b/pyexcel_odsr/odsr.py @@ -12,7 +12,7 @@ from pyexcel_io.sheet import SheetReader from pyexcel_io._compact import OrderedDict -from pyexcel_odsr.messyods import ODSTableSet +from pyexcel_odsr.messyods import ODSTableSet, FODSTableSet import pyexcel_odsr.converter as converter @@ -104,6 +104,31 @@ def _load_from_file(self): self._native_book = ODSTableSet(self._file_name) +class FODSBook(BookReader): + """read ods book""" + def open(self, file_name, **keywords): + """open fods file""" + BookReader.open(self, file_name, **keywords) + self._load_from_file() + + def read_all(self): + """read all sheets""" + result = OrderedDict() + for sheet in self._native_book.make_tables(): + ods_sheet = ODSSheet(sheet, **self._keywords) + result[ods_sheet.name] = ods_sheet.to_array() + + return result + + def read_sheet(self, native_sheet): + """read one native sheet""" + sheet = ODSSheet(native_sheet, **self._keywords) + return {sheet.name: sheet.to_array()} + + def _load_from_file(self): + self._native_book = FODSTableSet(self._file_name) + + def is_integer_ok_for_xl_float(value): """check if a float had zero value in digits""" return value == math.floor(value) diff --git a/tests/fixtures/ods_formats.fods b/tests/fixtures/ods_formats.fods new file mode 100644 index 0000000..ab4e208 --- /dev/null +++ b/tests/fixtures/ods_formats.fods @@ -0,0 +1,477 @@ + + + + Chenfu Wang2014-09-29T22:29:39.262016-01-10T13:58:22.783610277PT22H18S18LibreOffice/5.3.4.2$Windows_x86 LibreOffice_project/f82d347ccc0be322489bf7da61d7e4ad13fe2ff3 + + + 0 + 0 + 22665 + 2709 + + + view1 + + + 1 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 93 + 60 + true + false + + + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 93 + 60 + true + false + + + 0 + 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 93 + 60 + true + false + + + Sheet1 + 1856 + 0 + 93 + 60 + false + true + true + true + 12632256 + true + true + true + true + false + false + false + 1000 + 1000 + 1 + 1 + true + false + + + + + true + true + true + true + 12632256 + true + false + true + 3 + true + false + false + 1000 + 1000 + 1 + 1 + true + true + true + Microsoft Print to PDF + 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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAENPTVBBVF9EVVBMRVhfTU9ERRMARHVwbGV4TW9kZTo6VW5rbm93bg== + 0 + false + true + true + false + false + false + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + £ + + + + + - + £ + + + + + + + + + + + - + + + + + + + £ + + + + - + £ + + + + + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + % + + + + / + + / + + + + + : + + : + + + + + : + + : + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ??? + + + + Page 1 + + + + + + + ??? (???) + + + 00/00/0000, 00:00:00 + + + + + Page 1 / 99 + + + + + + + + + + + + + + + + + + + + Date + + + Time + + + Boolean + + + Float + + + Currency + + + Percentage + + + Int + + + Scientific + + + Fractions + + + Text + + + + + 11/11/14 + + + 11:12:12 + + + TRUE + + + 11.11 + + + £1 + + + 200.00% + + + 3 + + + 1.00E+005 + + + 1 1/4 + + + abc + + + + + 1/1/01 + + + 00:00:12 + + + FALSE + + + + -£10,000 + + + + + + + 0 + + + + + + + 27:17:54 + + + + 11 + + + + + + + Other + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_fods_reader.py b/tests/test_fods_reader.py new file mode 100644 index 0000000..004bfc1 --- /dev/null +++ b/tests/test_fods_reader.py @@ -0,0 +1,16 @@ +import os +from pyexcel_odsr.odsr import FODSBook + +from base import ODSCellTypes + + +class TestFODSReader(ODSCellTypes): + def setUp(self): + r = FODSBook() + r.open(os.path.join("tests", + "fixtures", + "ods_formats.fods")) + self.data = r.read_all() + for key in self.data.keys(): + self.data[key] = list(self.data[key]) + r.close()