# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """Rendering a week, a month or a year. Same rules as the day block: plain text, no padding, and the count separated from the name by a dash rather than by spaces that only line up in a terminal. Longer than the nightly post on purpose. These are asked for, not pushed, so somebody who typed `!miesiąc` has already agreed to read a month. """ from __future__ import annotations from typing import TYPE_CHECKING, Final from signal_bot.stats import text from signal_bot.stats.periods import pl_day_month if TYPE_CHECKING: from signal_bot.stats.periods import Period from signal_bot.stats.queries_day import DayRow from signal_bot.stats.queries_period import Trend MEDALS: Final = ("\U0001f947", "\U0001f948", "\U0001f949") CHART: Final = "\U0001f4ca" BAR_WIDTH: Final = 10 MAX_ROWS: Final = 12 def bar(share: int) -> str: """A share of voice as a text bar. Blocks, because Signal has no charts.""" filled = round(share / 100 * BAR_WIDTH) return "█" * filled + "░" * (BAR_WIDTH - filled) def trend_line(trend: Trend, what: str) -> str: """One line comparing a period with the one before it. Says "first one" rather than "0%" when there is no baseline: the group should not be told a period is unchanged when there is nothing to have changed from. """ if trend.percent is None: return f"({what}: pierwszy taki okres w historii)" if not trend.delta: return f"({what}: dokładnie tyle samo co poprzednio)" direction = "więcej" if trend.delta > 0 else "mniej" return f"{trend.arrow} {abs(trend.percent)}% {direction} niż {what}" def _row(row: DayRow, place: int, total: int, *, podium: bool) -> str: """One person's line, with a medal or a rank and a share bar.""" lead = MEDALS[place - 1] if podium and place <= len(MEDALS) else f"{place}." share = round(row.messages / total * 100) if total else 0 return f"{lead} {row.display_name} — {row.messages} {bar(share)} {share}%" def render_period( period: Period, rows: list[DayRow], trend: Trend, *, since: str | None = None, podium: bool = False, ) -> str: """Compose a period summary.""" total = sum(row.messages for row in rows) media = sum(row.media for row in rows) extra = f" ({media} \U0001f4f7)" if media else "" lines = [f"{CHART} {period.label} — {text.messages(total)}{extra}"] if not rows: lines.append("Nikt nic nie napisał.") return "\n".join(lines) shown = rows[:MAX_ROWS] lines.extend(_row(row, i + 1, total, podium=podium) for i, row in enumerate(shown)) if len(rows) > MAX_ROWS: hidden = rows[MAX_ROWS:] rest = sum(row.messages for row in hidden) lines.append(f"…i {text.people(len(hidden))} więcej (razem {text.messages(rest)})") lines.append(trend_line(trend, "poprzednio")) if since: lines.append(f"(liczymy od {pl_day_month(since)})") return "\n".join(lines)