"""Экспорт отчётов: генерация CSV, история.""" import subprocess import flet as ft from app.core import reports as rep from app.ui.components import snack class ReportsPage(ft.Column): def __init__(self, page: ft.Page, go): super().__init__(expand=True, scroll=ft.ScrollMode.AUTO, spacing=12) self._page = page self.status = ft.Text("", size=13, color=ft.Colors.GREY_500) self.list_col = ft.Column(spacing=4) self.controls = [ ft.Text("Отчёты", size=28, weight=ft.FontWeight.BOLD), ft.Text("CSV со списком серверов, статусом онлайн, метриками и последними задачами.", size=13, color=ft.Colors.GREY_500), ft.Row([ ft.FilledButton("Сгенерировать отчёт", icon=ft.Icons.SUMMARIZE, on_click=self._generate), ft.IconButton(ft.Icons.REFRESH, on_click=lambda e: self._reload()), ]), self.status, ft.Divider(), ft.Text("История отчётов", weight=ft.FontWeight.BOLD, size=16), self.list_col, ] self._reload() def _generate(self, e): self.status.value = "генерация…" self._page.update() async def _do(): path = await rep.generate() self.status.value = f"сохранён: {path}" self._reload() self._page.run_task(_do) def _reload(self): self.list_col.controls = [] for p in rep.list_reports(): size_kb = p.stat().st_size // 1024 self.list_col.controls.append( ft.ListTile( leading=ft.Icon(ft.Icons.DESCRIPTION), title=ft.Text(p.name, size=13), subtitle=ft.Text(f"{size_kb} КБ", size=11), dense=True, trailing=ft.IconButton( ft.Icons.OPEN_IN_NEW, tooltip="Открыть", on_click=lambda e, p=p: self._open(p)), ) ) if not self.list_col.controls: self.list_col.controls.append( ft.Text("отчётов пока нет", color=ft.Colors.GREY_500)) self._page.update() def _open(self, path): try: subprocess.Popen(["explorer", "/select,", str(path)]) except Exception as ex: snack(self._page, f"не удалось открыть: {ex}", error=True)