feat: add Excel export functionality with support for metadata and customizable sheets

This commit is contained in:
2025-11-10 18:32:09 +01:00
parent 5f183faa63
commit 4b33a5dba3
2 changed files with 189 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass
from datetime import date, datetime, timezone
from decimal import Decimal
from io import BytesIO
from typing import Any, Iterable
import pytest
@@ -10,15 +11,19 @@ import pytest
from services.export_serializers import (
CSVExportColumn,
CSVExporter,
ExcelExporter,
default_formatter,
default_project_columns,
default_scenario_columns,
export_projects_to_excel,
export_scenarios_to_excel,
format_date_iso,
format_datetime_utc,
format_decimal,
stream_projects_to_csv,
stream_scenarios_to_csv,
)
from openpyxl import load_workbook
@dataclass(slots=True)
@@ -50,6 +55,11 @@ def collect_csv_bytes(chunks: Iterable[bytes]) -> list[str]:
return [chunk.decode("utf-8") for chunk in chunks]
def load_workbook_bytes(data: bytes):
buffer = BytesIO(data)
return load_workbook(buffer, read_only=True, data_only=True)
def test_csv_exporter_writes_header_and_rows() -> None:
exporter = CSVExporter(
[
@@ -66,6 +76,64 @@ def test_csv_exporter_writes_header_and_rows() -> None:
assert chunks[1] == "Alpha,Nevada\n"
def test_excel_exporter_basic_workbook() -> None:
exporter = ExcelExporter(default_project_columns(), sheet_name="Projects")
project = DummyProject(name="Alpha", location="Nevada")
data = exporter.export([project])
workbook = load_workbook_bytes(data)
sheet = workbook["Projects"]
rows = list(sheet.rows)
assert [cell.value for cell in rows[0]] == [
"name",
"location",
"operation_type",
"description",
"created_at",
"updated_at",
]
assert rows[1][0].value == "Alpha"
def test_excel_export_projects_helper_with_metadata() -> None:
project = DummyProject(name="Alpha", location="Nevada")
data = export_projects_to_excel(
[project], metadata={"rows": 1}, workbook_title="Project Export")
workbook = load_workbook_bytes(data)
assert workbook.properties.title == "Project Export"
assert "Projects" in workbook.sheetnames
assert any(sheet.title.startswith("Metadata")
for sheet in workbook.worksheets)
def test_excel_export_scenarios_helper_projects_resolved() -> None:
project = DummyProject(name="Alpha")
scenario = DummyScenario(project=project, name="Scenario 1")
data = export_scenarios_to_excel([scenario])
workbook = load_workbook_bytes(data)
sheet = workbook["Scenarios"]
rows = list(sheet.rows)
assert rows[1][0].value == "Alpha"
assert rows[1][1].value == "Scenario 1"
exporter = CSVExporter(
[
CSVExportColumn("Name", "name"),
CSVExportColumn("Location", "location"),
]
)
project = DummyProject(name="Alpha", location="Nevada")
chunks = collect_csv_bytes(exporter.iter_bytes([project]))
assert chunks[0] == "Name,Location\n"
assert chunks[1] == "Alpha,Nevada\n"
def test_csv_exporter_handles_optional_values_and_default_formatter() -> None:
exporter = CSVExporter(
[