from __future__ import annotations from dataclasses import dataclass from pathlib import Path from typing import Iterable from openpyxl import Workbook from openpyxl.styles import Alignment, Font, PatternFill from openpyxl.utils import get_column_letter 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] def write_report(rows: Iterable[ReportRow], destination: Path) -> None: row_list = list(rows) columns = list(FIXED_REPORT_COLUMNS) workbook = Workbook() sheet = workbook.active sheet.title = "Отчет" sheet.freeze_panes = "A2" _write_header(sheet, columns) for row_index, row in enumerate(row_list, start=2): 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, ) for col_index, column_name in enumerate(columns, start=1): cell = sheet.cell( row=row_index, column=col_index, 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" _auto_width(sheet, columns) destination.parent.mkdir(parents=True, exist_ok=True) workbook.save(destination) def _write_header(sheet, columns: list[str]) -> None: fill = PatternFill(start_color="E2F0D9", end_color="E2F0D9", fill_type="solid") for index, name in enumerate(columns, start=1): cell = sheet.cell(row=1, column=index, value=name) cell.font = Font(bold=True) cell.alignment = Alignment( horizontal="center", vertical="center", wrap_text=True ) cell.fill = fill def _auto_width(sheet, columns: list[str]) -> None: for index, column_name in enumerate(columns, start=1): width = max(12, min(60, len(column_name) + 4)) column_letter = get_column_letter(index) sheet.column_dimensions[column_letter].width = width