Files
odoo_source/addons/base_import/odf_ods_reader.py
T
Xavier Morel f5a1693f06 [ADD] base_import: support for XLS and ODS files
* added intermediate ``_read_file`` step dispatching between CSV, ODS
  and XLS(X) parsing
  - primary dispatch on mime type, secondary on file extension:
    + OS may not provide a relevant mime type if no software locally
      installed for the filetype (e.g. Windows sends excel files as
      application/octet-stream if excel is not installed, and ODS files as
      zip if OpenOffice/LibreOffice isn't installed)
    + applications may re-register extensions to non-standard mimetypes
      breaking the dispatcher

    So if the mimetype is found trust it, otherwise try with the
    filename's extension (if any)
  - all readers skip lines with only empty cells in their output
  - ODS and XLS content are "CSVified", rows are converted to arrays of
    unicode strings
* UI altered to not assume CSV files everywhere, and avoid returning
  garbage preview data for non-CSV imports

Various:
* added a ``can_import`` utility function to tests.common, can be used
  to skip tests if an optional Python dependency is not installed (but
  one would like tests to run if the dependency is available)
* fixed datetime issue in ir_fields
* added converter for monetary fields (iso float)
* spreadsheet don't have integers, twiddling required to ensure integral
  values won't be serialized as floats (breaking conversion back to
  Python)
* improved some tests by asserting no error is generated (bonus: logs
  the error message if there is one, rather than just saying the result
  is blown)
* because some systems only provide elderly versions of
  XLRD (e.g. current debian stable provides 0.9.2 from April 2013)
  - keep using xldate_as_tuple instead of 0.9.3's xldate_as_datetime,
    workaround is simple
  - check for xlrd.xlsx in case of pre-0.8 XLRD

Authorship:
Ronak Baxi <rba@odoo.com>
Mohammed Shekha <msh@openerp.com>

Task 10792
Closes #7285
2015-08-26 16:50:09 +02:00

99 lines
3.5 KiB
Python

# Copyright 2011 Marco Conti
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# sourced from https://github.com/marcoconti83/read-ods-with-odfpy
# further altered locally
from odf import opendocument
from odf.table import Table, TableRow, TableCell
from odf.text import P
class ODSReader(object):
# loads the file
def __init__(self, file=None, content=None, clonespannedcolumns=None):
if not content:
self.clonespannedcolumns = clonespannedcolumns
self.doc = opendocument.load(file)
else:
self.clonespannedcolumns = clonespannedcolumns
self.doc = content
self.SHEETS = {}
for sheet in self.doc.spreadsheet.getElementsByType(Table):
self.readSheet(sheet)
# reads a sheet in the sheet dictionary, storing each sheet as an
# array (rows) of arrays (columns)
def readSheet(self, sheet):
name = sheet.getAttribute("name")
rows = sheet.getElementsByType(TableRow)
arrRows = []
# for each row
for row in rows:
arrCells = []
cells = row.getElementsByType(TableCell)
# for each cell
for count, cell in enumerate(cells, start=1):
# repeated value?
repeat = 0
if count != len(cells):
repeat = cell.getAttribute("numbercolumnsrepeated")
if not repeat:
repeat = 1
spanned = int(cell.getAttribute('numbercolumnsspanned') or 0)
# clone spanned cells
if self.clonespannedcolumns is not None and spanned > 1:
repeat = spanned
ps = cell.getElementsByType(P)
textContent = u""
# for each text/text:span node
for p in ps:
for n in p.childNodes:
if n.nodeType == 1 and n.tagName == "text:span":
for c in n.childNodes:
if c.nodeType == 3:
textContent = u'{}{}'.format(textContent, n.data)
if n.nodeType == 3:
textContent = u'{}{}'.format(textContent, n.data)
if textContent:
if not textContent.startswith("#"): # ignore comments cells
for rr in range(int(repeat)): # repeated?
arrCells.append(textContent)
else:
for rr in range(int(repeat)):
arrCells.append("")
# if row contained something
if arrCells:
arrRows.append(arrCells)
#else:
# print ("Empty or commented row (", row_comment, ")")
self.SHEETS[name] = arrRows
# returns a sheet as an array (rows) of arrays (columns)
def getSheet(self, name):
return self.SHEETS[name]
def getFirstSheet(self):
return next(iter(self.SHEETS.itervalues()))