import base64 import flet as ft import designer as ds import api_client from radar_chart import RadarChart class ResponsePage(ft.Column): def __init__(self, page: ft.Page, response_id: str): self._page = page self._response_id = response_id self._loading = ft.ProgressRing(width=40, height=40, color=ds.clolors.laer1) super().__init__( alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.STRETCH, spacing=8, controls=[ ft.Row(alignment=ft.MainAxisAlignment.CENTER, controls=[self._loading]) ], ) async def load(self): response = await api_client.get_response(self._response_id) if not response: self.controls = [ds.CastomText(text="Результат не найден", size=14, color="#FF6B6B")] self.update() return poll = await api_client.get_poll(response["poll_id"]) if not poll: self.controls = [ds.CastomText(text="Тест не найден", size=14, color="#FF6B6B")] self.update() return radar = await api_client.get_radar_result(self._response_id) self._render(response, poll, radar) self.update() def _render(self, response: dict, poll: dict, radar: dict | None): questions_map = {q["id"]: q for q in poll.get("questions", [])} submitted_at = response["submitted_at"][:19].replace("T", " ") controls: list[ft.Control] = [ ft.Container( padding=ft.padding.only(bottom=8), content=ft.Column(spacing=4, controls=[ ds.CastomText(text=poll.get("title", ""), size=20, weight=ft.FontWeight.BOLD), ds.CastomText(text=f"Пройден: {submitted_at}", size=12), ]), ) ] # ── Паутинная диаграмма ────────────────────────────────────────────── if radar and radar.get("items"): items = sorted( radar["items"], key=lambda x: (x.get("dimension_position") is None, x.get("dimension_position", 0)), ) actual_max = max((it["value"] for it in items), default=1.0) max_val = max(actual_max, 14.0) async def _download(_evt, _rid=self._response_id): svg_bytes = await api_client.get_radar_svg_bytes(_rid) if svg_bytes: b64 = base64.b64encode(svg_bytes).decode() uri = f"data:image/svg+xml;base64,{b64}" self._page.launch_url(uri, web_window_name="_blank") controls.append(ft.Container( border_radius=16, padding=12, bgcolor=ds.clolors.laer3, content=ft.Column( horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=10, controls=[ ds.CastomText(text="Результат", size=16, weight=ft.FontWeight.BOLD), ft.Row( alignment=ft.MainAxisAlignment.CENTER, controls=[RadarChart(items, max_val)], ), ft.Row( alignment=ft.MainAxisAlignment.CENTER, controls=[ ft.ElevatedButton( text="Сохранить диаграмму", icon=ft.Icons.DOWNLOAD, on_click=_download, style=ft.ButtonStyle( color=ds.clolors.ferst, bgcolor=ds.clolors.laer2, ), ) ], ), ], ), )) # ── Ответы ─────────────────────────────────────────────────────────── answer_cards: list[ft.Control] = [] for answer in response.get("answers", []): q = questions_map.get(answer["question_id"]) if not q: continue choice_rows = [] for c in q.get("choices", []): is_selected = c["id"] == answer.get("choice_id") choice_rows.append(ft.Container( border_radius=8, padding=ft.padding.symmetric(horizontal=12, vertical=6), bgcolor=ds.clolors.laer1 if is_selected else ds.clolors.laer3, content=ft.Row(spacing=8, controls=[ ft.Icon( ft.Icons.CHECK_CIRCLE if is_selected else ft.Icons.RADIO_BUTTON_UNCHECKED, color=ds.clolors.ferst, size=16, ), ds.CastomText(text=c["text"], size=13), ]), )) answer_cards.append(ft.Container( border_radius=12, padding=16, bgcolor=ds.clolors.laer2, content=ft.Column(spacing=8, controls=[ ds.CastomText(text=q.get("text", ""), size=14, weight=ft.FontWeight.BOLD), *choice_rows, ]), )) if answer_cards: controls.append(ds.CastomText(text="Ответы", size=16, weight=ft.FontWeight.BOLD)) controls.extend(answer_cards) self.controls = controls