signal-bot/tests/test_stats_curiosities.py
Krzysztof kuhy Rudnicki c195e6b4c1
Some checks are pending
CI / check (push) Waiting to run
CI / web (push) Waiting to run
Funny numbers: 69 (nice), 2137 🙏 and five more on every count the stats print
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>
2026-09-04 21:55:33 +02:00

238 lines
8.7 KiB
Python

# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence.
"""What the bot notices about a day, and what it stays quiet about."""
from __future__ import annotations
from typing import TYPE_CHECKING
from signal_bot.stats import curiosities
from signal_bot.stats.facts import DayFacts, gather
from signal_bot.stats.highlights import Highlight, ratio, select
from signal_bot.stats.queries_day import day_rows, with_silent
from tests.stats_seed import ALA, BASIA, GROUP, KUHY, Shape, wrote
if TYPE_CHECKING:
from signal_bot.store import Store
def facts_for(store: Store, day: str, roster: list[tuple[str, str]] | None = None) -> DayFacts:
"""Gather a day the way publish_report does."""
rows = with_silent(day_rows(store.events, GROUP, day), roster or [])
return gather(store.events, GROUP, day, rows)
def test_day_one_notices_nothing(store: Store) -> None:
"""Almost every candidate has no history to stand against yet."""
wrote(store, "2026-09-01", KUHY, 3)
fired = [c for c in curiosities.for_day(facts_for(store, "2026-09-01")) if c]
assert [c.kind for c in fired] == ["only_one_spoke"]
def test_facts_expose_the_days_crown_holders(store: Store) -> None:
wrote(store, "2026-09-01", ALA, 4)
wrote(store, "2026-09-01", KUHY, 4)
wrote(store, "2026-09-01", BASIA, 1)
assert sorted(n for _, n in facts_for(store, "2026-09-01").leaders) == ["Ala", "kuhy"]
assert facts_for(store, "2026-09-09").leaders == []
def test_a_personal_record_reports_what_it_beat(store: Store) -> None:
wrote(store, "2026-09-01", ALA, 5)
wrote(store, "2026-09-02", KUHY, 1)
wrote(store, "2026-09-02", ALA, 9)
found = curiosities.personal_record(facts_for(store, "2026-09-02"))
assert found is not None
assert "Ala 9 — rekord życiowy (poprzedni 5, 1 września)" in found.text
def test_a_first_ever_day_is_not_announced_as_a_record(store: Store) -> None:
"""Everybody's first day is their best; saying so every time is noise."""
wrote(store, "2026-09-01", ALA, 5)
assert curiosities.personal_record(facts_for(store, "2026-09-01")) is None
def test_the_busiest_day_ever(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 3)
wrote(store, "2026-09-02", KUHY, 9)
found = curiosities.group_record_day(facts_for(store, "2026-09-02"))
assert found is not None
assert "najgadatliwszy dzień w historii: 9 wiadomości" in found.text
def test_a_quiet_day_is_not_a_record(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 9)
wrote(store, "2026-09-02", KUHY, 3)
assert curiosities.group_record_day(facts_for(store, "2026-09-02")) is None
def test_a_media_record_needs_more_than_one_photo(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 3, Shape(media=1))
assert curiosities.media_record(facts_for(store, "2026-09-01")) is None
wrote(store, "2026-09-02", KUHY, 4, Shape(media=3))
found = curiosities.media_record(facts_for(store, "2026-09-02"))
assert found is not None
assert "rekord zdjęć: 3" in found.text
def test_a_crown_streak_needs_two_days(store: Store) -> None:
wrote(store, "2026-09-01", ALA, 5)
wrote(store, "2026-09-01", KUHY, 1)
assert curiosities.crown_streak(facts_for(store, "2026-09-01")) is None
wrote(store, "2026-09-02", ALA, 5)
wrote(store, "2026-09-02", KUHY, 1)
found = curiosities.crown_streak(facts_for(store, "2026-09-02"))
assert found is not None
assert "Ala z koroną 2 dni z rzędu" in found.text
def test_a_shared_crown_names_everyone(store: Store) -> None:
for day in ("2026-09-01", "2026-09-02"):
wrote(store, day, ALA, 4)
wrote(store, day, KUHY, 4)
found = curiosities.crown_streak(facts_for(store, "2026-09-02"))
assert found is not None
assert "Ala i kuhy z koroną" in found.text
def test_breaking_a_silence(store: Store) -> None:
wrote(store, "2026-09-01", BASIA, 2)
for day in ("2026-09-02", "2026-09-03", "2026-09-04"):
wrote(store, day, KUHY, 3)
wrote(store, "2026-09-05", BASIA, 1)
wrote(store, "2026-09-05", KUHY, 9)
found = curiosities.silence_broken(facts_for(store, "2026-09-05"))
assert found is not None
assert "Basia odezwał się po 3 dni ciszy" in found.text
def test_a_short_gap_is_not_worth_mentioning(store: Store) -> None:
wrote(store, "2026-09-01", BASIA, 2)
wrote(store, "2026-09-02", KUHY, 3)
wrote(store, "2026-09-03", BASIA, 2)
assert curiosities.silence_broken(facts_for(store, "2026-09-03")) is None
def test_a_monologue(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 7)
found = curiosities.only_one_spoke(facts_for(store, "2026-09-01"))
assert found is not None
assert "cały dzień mówił tylko kuhy" in found.text
def test_one_single_message_is_not_a_monologue(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 1)
assert curiosities.only_one_spoke(facts_for(store, "2026-09-01")) is None
def test_dominating_the_day(store: Store) -> None:
wrote(store, "2026-09-01", ALA, 8)
wrote(store, "2026-09-01", KUHY, 2)
found = curiosities.dominant_share(facts_for(store, "2026-09-01"))
assert found is not None
assert "Ala to 80% dzisiejszych wiadomości" in found.text
def test_an_evenly_shared_day_is_not_dominated(store: Store) -> None:
wrote(store, "2026-09-01", ALA, 5)
wrote(store, "2026-09-01", KUHY, 4)
wrote(store, "2026-09-01", BASIA, 4)
assert curiosities.dominant_share(facts_for(store, "2026-09-01")) is None
def test_a_weekday_needs_a_baseline_before_it_can_be_unusual(store: Store) -> None:
"""Two Tuesdays are not an average worth quoting."""
wrote(store, "2026-09-01", KUHY, 5)
wrote(store, "2026-09-08", KUHY, 50)
assert curiosities.weekday_average(facts_for(store, "2026-09-08")) is None
def test_an_unusual_weekday(store: Store) -> None:
for day in ("2026-09-01", "2026-09-08", "2026-09-15"):
wrote(store, day, KUHY, 10)
wrote(store, "2026-09-22", KUHY, 30)
found = curiosities.weekday_average(facts_for(store, "2026-09-22"))
assert found is not None
assert "wtorki mają średnio 10 — dziś o 200% więcej" in found.text
def test_a_typical_weekday_says_nothing(store: Store) -> None:
for day in ("2026-09-01", "2026-09-08", "2026-09-15", "2026-09-22"):
wrote(store, day, KUHY, 10)
assert curiosities.weekday_average(facts_for(store, "2026-09-22")) is None
def test_a_quiet_weekday_reads_as_fewer(store: Store) -> None:
for day in ("2026-09-01", "2026-09-08", "2026-09-15"):
wrote(store, day, KUHY, 20)
wrote(store, "2026-09-22", KUHY, 5)
found = curiosities.weekday_average(facts_for(store, "2026-09-22"))
assert found is not None
assert "mniej" in found.text
def test_a_night_owl(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 3, Shape(hour=1))
found = curiosities.night_owl(facts_for(store, "2026-09-01"))
assert found is not None
assert "kuhy pisał o 01:00" in found.text
def test_an_ordinary_evening_is_not_a_night_owl(store: Store) -> None:
wrote(store, "2026-09-01", KUHY, 3, Shape(hour=22))
assert curiosities.night_owl(facts_for(store, "2026-09-01")) is None
def test_one_line_per_family(store: Store) -> None:
"""Three sentences about the same person are one fact, not three."""
picks = select(
[
Highlight("a", "voice", 60, 1.0, "A"),
Highlight("b", "voice", 50, 1.0, "B"),
Highlight("c", "record", 40, 1.0, "C"),
],
3,
)
assert [p.kind for p in picks] == ["a", "c"]
def test_selection_is_ordered_and_bounded() -> None:
picks = select(
[
Highlight("low", "one", 10, 1.0, ""),
None,
Highlight("high", "two", 90, 1.0, ""),
Highlight("mid", "three", 50, 1.0, ""),
],
2,
)
assert [p.kind for p in picks] == ["high", "mid"]
def test_ties_break_on_name_so_a_day_renders_the_same_twice() -> None:
picks = select(
[
Highlight("zebra", "one", 50, 1.0, ""),
Highlight("alpha", "two", 50, 1.0, ""),
],
2,
)
assert [p.kind for p in picks] == ["alpha", "zebra"]
def test_nothing_to_say_is_an_empty_list() -> None:
assert select([None, None], 3) == []
def test_ratio_survives_a_zero_baseline() -> None:
assert ratio(5, 0) == 5.0
assert ratio(10, 5) == 1.0
def test_a_record_that_lands_on_a_funny_number_carries_its_marker(store: Store) -> None:
"""The count is what is funny; the marker rides on it wherever the day's notice prints it."""
wrote(store, "2026-09-01", ALA, 69)
wrote(store, "2026-09-02", ALA, 2137)
found = curiosities.personal_record(facts_for(store, "2026-09-02"))
assert found is not None
assert "Ala 2137 🙏 — rekord życiowy (poprzedni 69 (nice), 1 września)" in found.text