diff --git a/src/voc4cat/utils.py b/src/voc4cat/utils.py index bd6d6a0..b7c46d7 100644 --- a/src/voc4cat/utils.py +++ b/src/voc4cat/utils.py @@ -1,10 +1,13 @@ import glob import logging import os +from copy import copy from pathlib import Path -from openpyxl import load_workbook # as _load_workbook +from openpyxl import load_workbook +from openpyxl.utils.cell import coordinate_from_string from openpyxl.workbook.workbook import Workbook +from openpyxl.worksheet.table import Table from voc4cat.checks import Voc4catError @@ -80,3 +83,39 @@ def has_file_in_multiple_formats(dir_): return False seen = set() return [x for x in file_names if x in seen or seen.add(x)] + + +def adjust_length_of_tables(wb_path): + """Expand length of all tables in workbook to include all filled rows + + For all tables in the workbook the table is expanded to include the last + row with content. Note that tables are not shrunk by this method if they + contain empty rows at the end. + """ + wb = load_workbook(wb_path) + + for ws in wb.sheetnames: + for t_name in list(wb[ws].tables): + old_range = wb[ws].tables[t_name].ref # "A2:I20" + start, end = old_range.split(":") + end_col, _end_row = coordinate_from_string(end) + adjusted = f"{start}:{end_col}{wb[ws].max_row}" + if adjusted == old_range: + continue + # Expanding the table is not possible with openpyxl. Instead a too + # short table is removed, and a new adjusted table is created. + style = copy(wb[ws].tables[t_name].tableStyleInfo) + del wb[ws].tables[t_name] + newtab = Table(displayName=t_name, ref=adjusted) + newtab.tableStyleInfo = style + wb[ws].add_table(newtab) + logger.debug( + 'Adjusted table "%s" in sheet "%s" from {%s} to {%s}.', + t_name, + wb[ws].title, + old_range, + adjusted, + ) + + wb.save(wb_path) + wb.close() diff --git a/tests/test_utils.py b/tests/test_utils.py index fe6bf04..3c03ecf 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,6 @@ -from voc4cat.utils import split_and_tidy +from openpyxl import Workbook, load_workbook +from openpyxl.worksheet.table import Table +from voc4cat.utils import adjust_length_of_tables, split_and_tidy def test_default_action(): @@ -11,3 +13,27 @@ def test_default_action(): def test_trailing_comma(): assert split_and_tidy("a,") == ["a"] assert split_and_tidy("a,b,") == ["a", "b"] + + +def test_expand_tables(tmp_path): + test_wb = tmp_path / "table.xlsx" + wb = Workbook() + ws = wb.active + ws.append(["Letter", "value"]) # table header + data = [ + ["A", 1], + ["B", 2], + ] + for row in data: + ws.append(row) + tab = Table(displayName="Table1", ref="A1:B3") + ws.add_table(tab) + ws["A5"] = "X" + wb.save(test_wb) + + adjust_length_of_tables(test_wb) + + wb = load_workbook(test_wb) + name, table_range = wb.active.tables.items()[0] + assert name == "Table1" + assert table_range == "A1:B5"