mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-10 19:37:03 +01:00
Seven numbers the group finds funny get a marker wherever they show up as
a count: the nightly table, every !stats_* view, !ja, the records and the
highlights. Percentages and shares stay bare (a 69% share is not a 69), and
so does the (N 📷) aside, so one line never carries two jokes. One table,
FUNNY in stats/text.py, and one helper, marked(), so adding a number is one
line. Documented in DOCS-statistics.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
3.0 KiB
Python
86 lines
3.0 KiB
Python
# 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} — {text.marked(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)
|