Compare commits
No commits in common. "f14ea0f1893dee4abefeea5c82e3ead36b59cab0" and "01fc7d4a0147e840a67a16b1d30644bf23cae4a0" have entirely different histories.
f14ea0f189
...
01fc7d4a01
@ -21,22 +21,6 @@ RowFormatResolver = Callable[[tuple, str, int], dict[str, Any] | None]
|
|||||||
ColumnFormatResolver = Callable[[str, int], dict[str, Any] | None]
|
ColumnFormatResolver = Callable[[str, int], dict[str, Any] | None]
|
||||||
|
|
||||||
|
|
||||||
def _hierarchical_rows(rows: list[tuple]):
|
|
||||||
"""Use the same row-type hierarchy as the realtime table, skipping META."""
|
|
||||||
levels = {"ROOT": 0, "GROUP": 1, "ITEM": 2, "SUB_ITEM": 3, "INPUT": 4}
|
|
||||||
stack: list[int] = []
|
|
||||||
for row in rows:
|
|
||||||
item_type = row[0] if row else None
|
|
||||||
if item_type == "META":
|
|
||||||
continue
|
|
||||||
level = levels.get(item_type, 999)
|
|
||||||
while stack and stack[-1] >= level:
|
|
||||||
stack.pop()
|
|
||||||
yield row, len(stack)
|
|
||||||
if item_type != "INPUT":
|
|
||||||
stack.append(level)
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractWriter(abc.ABC):
|
class AbstractWriter(abc.ABC):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -242,10 +226,8 @@ class DepthSheetWriter(AbstractWriter):
|
|||||||
def write(self, worksheet: Worksheet, rows: list[tuple]) -> None:
|
def write(self, worksheet: Worksheet, rows: list[tuple]) -> None:
|
||||||
column_widths: dict[int, int] = {}
|
column_widths: dict[int, int] = {}
|
||||||
self._write_header(worksheet, column_widths=column_widths)
|
self._write_header(worksheet, column_widths=column_widths)
|
||||||
worksheet.sheet_properties.outlinePr.summaryBelow = False
|
|
||||||
next_row = worksheet.max_row + 1
|
next_row = worksheet.max_row + 1
|
||||||
for row, outline_level in _hierarchical_rows(rows):
|
for row in rows:
|
||||||
worksheet.row_dimensions[next_row].outlineLevel = min(outline_level, 7)
|
|
||||||
if len(row) > 3 and isinstance(row[3], dict):
|
if len(row) > 3 and isinstance(row[3], dict):
|
||||||
if row[0] == 'META':
|
if row[0] == 'META':
|
||||||
continue
|
continue
|
||||||
@ -507,7 +489,6 @@ class XlsxDepthSheetWriter(AbstractWriter):
|
|||||||
column_widths: dict[int, int] = {}
|
column_widths: dict[int, int] = {}
|
||||||
header_rows = self._get_header_max_row(self.columns_dict)
|
header_rows = self._get_header_max_row(self.columns_dict)
|
||||||
self._write_header(worksheet, column_widths=column_widths)
|
self._write_header(worksheet, column_widths=column_widths)
|
||||||
worksheet.outline_settings(visible=True, symbols_below=False)
|
|
||||||
output_row = header_rows
|
output_row = header_rows
|
||||||
column_formats = None
|
column_formats = None
|
||||||
if self.column_format_resolver:
|
if self.column_format_resolver:
|
||||||
@ -518,8 +499,7 @@ class XlsxDepthSheetWriter(AbstractWriter):
|
|||||||
for column, column_key in enumerate(self.columns)
|
for column, column_key in enumerate(self.columns)
|
||||||
]
|
]
|
||||||
|
|
||||||
for row, outline_level in _hierarchical_rows(rows):
|
for row in rows:
|
||||||
worksheet.set_row(output_row, None, None, {"level": min(outline_level, 7)})
|
|
||||||
if len(row) > 3 and isinstance(row[3], dict):
|
if len(row) > 3 and isinstance(row[3], dict):
|
||||||
if row[0] == "META":
|
if row[0] == "META":
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -1,54 +1,13 @@
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
import pytest
|
from openpyxl import Workbook
|
||||||
from openpyxl import Workbook, load_workbook
|
|
||||||
from xlsxwriter import Workbook as XlsxWorkbook
|
|
||||||
|
|
||||||
from src.services.export_service import ExportService
|
from src.services.export_service import ExportService
|
||||||
from src.services.export_service.export_mappings import (
|
from src.services.export_service.export_mappings import (
|
||||||
FORM1_FLAT_COLUMNS,
|
FORM1_FLAT_COLUMNS,
|
||||||
SMETA_COLUMNS,
|
SMETA_COLUMNS,
|
||||||
)
|
)
|
||||||
from src.services.export_service.export_writers import (
|
from src.services.export_service.export_writers import TabularSheetWriter
|
||||||
DepthSheetWriter,
|
|
||||||
TabularSheetWriter,
|
|
||||||
XlsxDepthSheetWriter,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("backend", ["openpyxl", "xlsxwriter", "xlsxwriter_columns"])
|
|
||||||
def test_depth_writer_preserves_collapsible_row_hierarchy(backend):
|
|
||||||
stream = BytesIO()
|
|
||||||
workbook = Workbook() if backend == "openpyxl" else XlsxWorkbook(stream)
|
|
||||||
worksheet = workbook.active if backend == "openpyxl" else workbook.add_worksheet()
|
|
||||||
options = {}
|
|
||||||
if backend == "xlsxwriter_columns":
|
|
||||||
options["column_format_resolver"] = lambda key, column: {"bold": True}
|
|
||||||
writer_class = DepthSheetWriter if backend == "openpyxl" else XlsxDepthSheetWriter
|
|
||||||
if backend != "openpyxl":
|
|
||||||
options["workbook"] = workbook
|
|
||||||
writer = writer_class(
|
|
||||||
columns_dict={"header": {"name": "Header", "key": "", "children": {
|
|
||||||
"name": {"name": "Name"},
|
|
||||||
}}},
|
|
||||||
row_normalizer=lambda row: row,
|
|
||||||
value_serializer=lambda value: value,
|
|
||||||
**options,
|
|
||||||
)
|
|
||||||
# Missing GROUP/SUB_ITEM levels and misleading numeric depths match frontend cases.
|
|
||||||
types = ["ROOT", "ITEM", "INPUT", "META", "INPUT", "ITEM", "SUB_ITEM", "INPUT", "ROOT"]
|
|
||||||
writer.write(worksheet, [(kind, 42, index, {"name": str(index)}) for index, kind in enumerate(types)])
|
|
||||||
if backend == "openpyxl":
|
|
||||||
workbook.save(stream)
|
|
||||||
else:
|
|
||||||
workbook.close()
|
|
||||||
stream.seek(0)
|
|
||||||
sheet = load_workbook(stream).active
|
|
||||||
assert sheet.sheet_properties.outlinePr.summaryBelow is False
|
|
||||||
assert [sheet.row_dimensions[index].outlineLevel for index in range(3, 11)] == [0, 1, 2, 2, 1, 2, 3, 0]
|
|
||||||
assert [sheet.cell(index, 1).value for index in range(3, 11)] == ["0", "1", "2", "4", "5", "6", "7", "8"]
|
|
||||||
assert all(not sheet.row_dimensions[index].hidden for index in range(3, 11))
|
|
||||||
|
|
||||||
|
|
||||||
def test_tabular_sheet_writer_writes_headers_and_rows():
|
def test_tabular_sheet_writer_writes_headers_and_rows():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user