from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Iterable from openpyxl import Workbook from openpyxl.cell import WriteOnlyCell from openpyxl.styles import Alignment, Font, PatternFill from .mapping import FIXED_REPORT_COLUMNS, build_fixed_row @dataclass class ReportRow: file_name: str record_id: str operation_index: int operation_fields: dict[str, str] participant_fields: dict[str, str] class StreamingReportWriter: def __init__(self) -> None: self.columns = list(FIXED_REPORT_COLUMNS) self.workbook = Workbook(write_only=True) self.sheet = self.workbook.create_sheet(title="Отчет") self.sheet.freeze_panes = "A2" self.sheet.append(self._build_header_row()) def append_row(self, row: ReportRow) -> None: data = build_fixed_row( file_name=row.file_name, record_id=row.record_id, operation_index=row.operation_index, operation_fields=row.operation_fields, participant_fields=row.participant_fields, ) cells: list[WriteOnlyCell] = [] for column_name in self.columns: cell = WriteOnlyCell(self.sheet, value=data.get(column_name, "")) if "Дата" in column_name: cell.number_format = "DD.MM.YYYY" if "Сумм" in column_name or "Сумма" in column_name: cell.number_format = "#,##0.00" cells.append(cell) self.sheet.append(cells) def save(self, destination: Path) -> None: destination.parent.mkdir(parents=True, exist_ok=True) self.workbook.save(destination) def _build_header_row(self) -> list[WriteOnlyCell]: fill = PatternFill(start_color="E2F0D9", end_color="E2F0D9", fill_type="solid") result: list[WriteOnlyCell] = [] for name in self.columns: cell = WriteOnlyCell(self.sheet, value=name) cell.font = Font(bold=True) cell.alignment = Alignment( horizontal="center", vertical="center", wrap_text=True, ) cell.fill = fill result.append(cell) return result def write_report(rows: Iterable[ReportRow], destination: Path) -> None: writer = StreamingReportWriter() for row in rows: writer.append_row(row) writer.save(destination)