fix(hands,memory): the daily routine folds into one line and stops crowding out single events
This commit is contained in:
+58
-19
@@ -4,7 +4,7 @@ from pathlib import Path
|
||||
from beaver_gateway.conversations.texts import UserSaid
|
||||
from beaver_gateway.conversations.envelope import RecallContext
|
||||
|
||||
from beaver_agent.hands.calendars import Calendars, Feed
|
||||
from beaver_agent.hands.calendars import Calendars, Event, Feed
|
||||
from beaver_agent.memory.recall import (
|
||||
LABEL,
|
||||
Recall,
|
||||
@@ -12,6 +12,7 @@ from beaver_agent.memory.recall import (
|
||||
frontmatter_aliases,
|
||||
mentions,
|
||||
notes_head,
|
||||
routine_names,
|
||||
)
|
||||
|
||||
|
||||
@@ -207,42 +208,80 @@ def _calendar(text: str | None) -> Calendars:
|
||||
return Fake((Feed("личный", "https://cal.example/x.ics"),), tz="Europe/Warsaw")
|
||||
|
||||
|
||||
BUSY = _ics(
|
||||
def _daily(title: str, clock: str) -> str:
|
||||
return f"SUMMARY:{title}\nDTSTART;TZID=Europe/Warsaw:20260902T{clock}\nRRULE:FREQ=DAILY"
|
||||
|
||||
|
||||
SINGLES = (
|
||||
"SUMMARY:Созвон\nDTSTART:20260902T160000Z",
|
||||
"SUMMARY:Врач\nDTSTART:20260903T090000Z",
|
||||
"SUMMARY:Analiza matematyczna II - Egzamin\nDTSTART;VALUE=DATE-TIME:20260905T110000\n"
|
||||
"DESCRIPTION:Sala: 3\\nA23 - CW\\n\nLOCATION:ul. Piotrowo 2\\, Poznań",
|
||||
"SUMMARY:Врач\nDTSTART:20260903T090000Z",
|
||||
"SUMMARY:за окном\nDTSTART:20260925T080000Z",
|
||||
"SUMMARY:завтрак\nDTSTART;TZID=Europe/Warsaw:20260902T080000\nRRULE:FREQ=DAILY",
|
||||
"SUMMARY:ужин\nDTSTART;TZID=Europe/Warsaw:20260902T200000\nRRULE:FREQ=DAILY",
|
||||
)
|
||||
ROUTINE = (
|
||||
_daily("Завтрак", "080000"),
|
||||
_daily("Обед", "150000"),
|
||||
_daily("Ужин", "200000"),
|
||||
_daily("Перекус", "180000"),
|
||||
_daily("Wind down", "223000"),
|
||||
)
|
||||
BUSY = _ics(*SINGLES, *ROUTINE)
|
||||
|
||||
|
||||
def test_calendar_section_sorts_the_week_and_says_what_did_not_fit(
|
||||
def _section(recall: Recall, now: datetime, text: str = "привет") -> list[str]:
|
||||
block = recall.block(RecallContext(text=text, kind="master", now=now))
|
||||
assert block is not None
|
||||
return [ln for ln in block.splitlines() if "календар" in ln or ln.startswith(" -")]
|
||||
|
||||
|
||||
def test_calendar_shows_every_single_event_and_folds_the_daily_routine(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
recall = _vault(tmp_path, _calendar(BUSY))
|
||||
now = datetime(2026, 9, 1, 12, 0, tzinfo=UTC)
|
||||
block = recall.block(RecallContext(text="привет", kind="master", now=now))
|
||||
assert block is not None
|
||||
lines = [ln for ln in block.splitlines() if "календар" in ln or ln.startswith(" ")]
|
||||
assert lines[0] == ("🗓 календарь (7 дней), событий 16, данные на 2026-09-01 14:00:")
|
||||
assert lines[1:4] == [
|
||||
" - 09-02 08:00 завтрак",
|
||||
lines = _section(recall, now)
|
||||
assert lines == [
|
||||
"🗓 календарь (7 дней), разовых 3, повседневных 31, данные на 2026-09-01 14:00:",
|
||||
" - 09-02 18:00 Созвон",
|
||||
" - 09-02 20:00 ужин",
|
||||
" - 09-03 11:00 Врач",
|
||||
" - 09-05 11:00 Analiza matematyczna II - Egzamin · Sala: 3, ul. Piotrowo 2, Poznań",
|
||||
" - повседневных повторов: 31 (Завтрак, Обед, Перекус, Ужин, Wind down)",
|
||||
]
|
||||
assert (
|
||||
" - 09-05 11:00 Analiza matematyczna II - Egzamin · Sala: 3, ul. Piotrowo 2, Poznań"
|
||||
in lines
|
||||
)
|
||||
assert len([ln for ln in lines if ln.startswith(" - ")]) == 15
|
||||
assert lines[-1] == " … и ещё 1 в окне"
|
||||
# секция дневная, как и сроки досок: второй конверт за те же сутки её не несёт
|
||||
again = recall.block(RecallContext(text="и ещё", kind="master", now=now))
|
||||
assert again is None or "календарь" not in again
|
||||
|
||||
|
||||
def test_no_amount_of_routine_can_push_out_a_single_event(tmp_path: Path) -> None:
|
||||
noise = tuple(_daily(f"быт {i}", f"{i:02d}0000") for i in range(9, 24))
|
||||
recall = _vault(tmp_path, _calendar(_ics(*SINGLES, *noise)))
|
||||
now = datetime(2026, 9, 1, 12, 0, tzinfo=UTC)
|
||||
lines = _section(recall, now)
|
||||
assert lines[0].startswith("🗓 календарь (7 дней), разовых 3, повседневных 96")
|
||||
assert [ln for ln in lines if "Egzamin" in ln]
|
||||
assert (
|
||||
len([ln for ln in lines if ln.startswith(" - ")]) == 4
|
||||
) # 3 разовых + свёртка
|
||||
assert lines[-1].startswith(" - повседневных повторов: 96")
|
||||
assert "и др." in lines[-1]
|
||||
|
||||
|
||||
def test_routine_line_counts_and_names_up_to_five(tmp_path: Path) -> None:
|
||||
events = [
|
||||
Event(
|
||||
start=datetime(2026, 9, 2, 8, tzinfo=UTC),
|
||||
title=t,
|
||||
place="",
|
||||
all_day=False,
|
||||
routine=True,
|
||||
)
|
||||
for t in ("Завтрак", "Обед", "Завтрак", "Ужин")
|
||||
]
|
||||
assert routine_names(events) == "Завтрак, Обед, Ужин"
|
||||
assert routine_names(events, limit=2) == "Завтрак, Обед и др."
|
||||
|
||||
|
||||
def test_calendar_section_is_printed_even_when_the_week_is_empty(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user