import flet as ft import base64 import designer as ds import api_client 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=ds.s(40), height=ds.s(40), color=ds.clolors.laer1) super().__init__( alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.STRETCH, spacing=ds.s(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) svg_bytes = await api_client.get_radar_svg_bytes(self._response_id) if radar and radar.get("image_url") else None self._render(response, poll, radar, svg_bytes) self.update() def _render(self, response: dict, poll: dict, radar: dict | None, svg_bytes: bytes | None = 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=ds.s(8)), content=ft.Column(spacing=ds.s(4), controls=[ ds.CastomText(text=poll.get("title", ""), size=20, weight=ft.FontWeight.BOLD), ds.CastomText(text=f"Пройден: {submitted_at}", size=12), ]), ) ] # ── Паутинная диаграмма ────────────────────────────────────────────── if svg_bytes and radar and radar.get("image_url"): b64 = base64.b64encode(svg_bytes).decode() data_uri = f"data:image/svg+xml;base64,{b64}" download_url = api_client.build_radar_download_url(self._page.url, self._response_id) async def _download(_evt, _url=download_url): await self._page.launch_url(_url) controls.append(ft.Container( border_radius=ds.s(16), padding=ds.s(12), bgcolor=ds.clolors.laer3, content=ft.Column( horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=ds.s(10), controls=[ ds.CastomText(text="Результат", size=16, weight=ft.FontWeight.BOLD), ft.Container( expand=True, content=ft.Image( src=data_uri, expand=True, height=ds.s(380), fit=ft.BoxFit.CONTAIN, ), ), ft.Row( alignment=ft.MainAxisAlignment.CENTER, controls=[ ds.CastomIconButton( text="Сохранить диаграмму", icon=ft.Icons.DOWNLOAD, on_click=_download, ) ], ), ], ), )) # ── Ответы ─────────────────────────────────────────────────────────── 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=ds.s(8), padding=ft.padding.symmetric(horizontal=ds.s(12), vertical=ds.s(6)), bgcolor=ds.clolors.laer1 if is_selected else ds.clolors.laer3, content=ft.Row(spacing=ds.s(8), controls=[ ft.Icon( ft.Icons.CHECK_CIRCLE if is_selected else ft.Icons.RADIO_BUTTON_UNCHECKED, color=ds.clolors.ferst, size=ds.s(16), ), ft.Container(expand=True, content=ds.CastomText(text=c["text"], size=13)), ]), )) answer_cards.append(ft.Container( border_radius=ds.s(12), padding=ds.s(16), bgcolor=ds.clolors.laer2, content=ft.Column(spacing=ds.s(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