diff --git a/src/tablib/formats/_ods.py b/src/tablib/formats/_ods.py index 97cef201..f83b9e0b 100644 --- a/src/tablib/formats/_ods.py +++ b/src/tablib/formats/_ods.py @@ -110,22 +110,29 @@ def import_sheet(cls, dset, sheet, headers=True, skip_lines=0): dset.title = sheet.getAttribute('name') - def is_real_cell(cell): - return cell.hasChildNodes() or not cell.getAttribute('numbercolumnsrepeated') + def repeat(cell): + try: + n = int(cell.getAttribute("numbercolumnsrepeated")) + except (AttributeError, TypeError, ValueError): + n = 1 + + return [cls.read_cell(cell)] * n rows = (row for row in sheet.childNodes if row.tagName == "table:table-row") for i, row in enumerate(rows): if i < skip_lines: continue - row_vals = [cls.read_cell(cell) for cell in row.childNodes if is_real_cell(cell)] + row_vals = [c for cell in row.childNodes for c in repeat(cell)] if not row_vals: continue if i == skip_lines and headers: - dset.headers = row_vals + dset.headers = limit_row(row_vals) else: - if i > skip_lines and len(row_vals) < dset.width: - row_vals += [''] * (dset.width - len(row_vals)) + if i > skip_lines: + if len(row_vals) < dset.width: + row_vals += [''] * (dset.width - len(row_vals)) + row_vals = row_vals[:dset.width] dset.append(row_vals) @classmethod @@ -248,3 +255,18 @@ def detect(cls, stream): return True except Exception: return False + + +def limit_row(headers, limit=5): + """Limits the number of trailing empty row values""" + limited = [] + count = 0 + + for v in headers: + limited += [v] + count = count + 1 if v == "" else 0 + + if count == limit: + break + + return limited diff --git a/tests/files/columns_repeated.ods b/tests/files/columns_repeated.ods new file mode 100644 index 00000000..3829da97 Binary files /dev/null and b/tests/files/columns_repeated.ods differ diff --git a/tests/test_tablib.py b/tests/test_tablib.py index 7a89eb7b..adc54ed4 100755 --- a/tests/test_tablib.py +++ b/tests/test_tablib.py @@ -1260,7 +1260,16 @@ def test_ods_import_set_ragged(self): ods_source = Path(__file__).parent / 'files' / 'ragged.ods' with ods_source.open('rb') as fh: dataset = tablib.Dataset().load(fh, 'ods') - self.assertEqual(dataset.pop(), (1, '', True, '')) + self.assertEqual(dataset[0], (1, '', True) + ('',) * 6) + + def test_ods_import_columns_repeated(self): + ods = Path(__file__).parent / "files" / "columns_repeated.ods" + + with ods.open("rb") as fh: + dataset = tablib.Dataset().load(fh, "ods") + + self.assertEqual(dataset[0], ("a1", "", "", "d1")) + self.assertEqual(dataset[1], ("repeat",) * 4) def test_ods_unknown_value_type(self): # The ods file was trafficked to contain: