Send at most one grouped birthday reminder email
Collects birthdays in the next 7 days, groups them into sections (Today/Tomorrow/In N days), and sends a single email. Fixes #201.
This commit is contained in:
parent
ebceb4cb51
commit
97c0531a22
89
update.py
89
update.py
|
@ -21,6 +21,7 @@ import agenda.mail
|
|||
import agenda.thespacedevs
|
||||
import agenda.types
|
||||
import agenda.uk_holiday
|
||||
from agenda.event import Event
|
||||
from agenda.types import StrDict
|
||||
from web_view import app
|
||||
|
||||
|
@ -295,7 +296,11 @@ def update_gandi(config: flask.config.Config) -> None:
|
|||
|
||||
|
||||
def check_birthday_reminders(config: flask.config.Config) -> None:
|
||||
"""Check for upcoming birthdays and send email reminders."""
|
||||
"""Send at most one grouped birthday reminder email per day.
|
||||
|
||||
Collects birthdays in the next 7 days, groups them into sections
|
||||
(Today/Tomorrow/In N days), and sends a single email.
|
||||
"""
|
||||
today = date.today()
|
||||
data_dir = config["PERSONAL_DATA"]
|
||||
entities_file = os.path.join(data_dir, "entities.yaml")
|
||||
|
@ -303,55 +308,65 @@ def check_birthday_reminders(config: flask.config.Config) -> None:
|
|||
if not os.path.exists(entities_file):
|
||||
return
|
||||
|
||||
# Get upcoming birthdays (next 7 days)
|
||||
birthdays = agenda.birthday.get_birthdays(today, entities_file)
|
||||
upcoming_birthdays = []
|
||||
|
||||
for birthday in birthdays:
|
||||
days_until = (birthday.date - today).days
|
||||
# Collect next 7 days into a dict keyed by days-until
|
||||
by_days: dict[int, list[Event]] = {}
|
||||
for ev in birthdays:
|
||||
days_until = (ev.as_date - today).days
|
||||
if 0 <= days_until <= 7:
|
||||
upcoming_birthdays.append((birthday, days_until))
|
||||
by_days.setdefault(days_until, []).append(ev)
|
||||
|
||||
if not upcoming_birthdays:
|
||||
if not by_days:
|
||||
return
|
||||
|
||||
# Group birthdays by days until
|
||||
birthday_groups = {}
|
||||
for birthday, days_until in upcoming_birthdays:
|
||||
if days_until not in birthday_groups:
|
||||
birthday_groups[days_until] = []
|
||||
birthday_groups[days_until].append(birthday)
|
||||
# Build subject
|
||||
headings: list[str] = []
|
||||
if 0 in by_days:
|
||||
headings.append("today")
|
||||
if 1 in by_days:
|
||||
headings.append("tomorrow")
|
||||
others = sum(1 for k in by_days.keys() if k not in (0, 1))
|
||||
if others:
|
||||
plural = "s" if others != 1 else ""
|
||||
headings.append(f"{others} other{plural}")
|
||||
subject = (
|
||||
f"🎂 Birthday reminders ({', '.join(headings)})"
|
||||
if headings
|
||||
else "🎂 Birthday reminders"
|
||||
)
|
||||
|
||||
# Send reminder emails
|
||||
for days_until, birthdays in birthday_groups.items():
|
||||
if days_until == 0:
|
||||
subject = "🎂 Birthday Today!"
|
||||
elif days_until == 1:
|
||||
subject = "🎂 Birthday Tomorrow!"
|
||||
# Build body (UK style dates)
|
||||
lines: list[str] = ["Upcoming birthdays (next 7 days):", ""]
|
||||
for delta in sorted(by_days.keys()):
|
||||
if delta == 0:
|
||||
lines.append("Today")
|
||||
elif delta == 1:
|
||||
lines.append("Tomorrow")
|
||||
else:
|
||||
subject = f"🎂 Birthday in {days_until} days"
|
||||
lines.append(f"In {delta} days")
|
||||
|
||||
body_lines = ["Birthday Reminder:\n"]
|
||||
for birthday in birthdays:
|
||||
date_str = birthday.date.strftime("%A, %B %d, %Y")
|
||||
if days_until == 0:
|
||||
body_lines.append(f"Today: {birthday.title} - {date_str}")
|
||||
elif days_until == 1:
|
||||
body_lines.append(f"Tomorrow: {birthday.title} - {date_str}")
|
||||
else:
|
||||
body_lines.append(f"{date_str}: {birthday.title}")
|
||||
|
||||
body_lines.append(
|
||||
f"\nView all birthdays: https://edwardbetts.com/agenda/birthdays"
|
||||
entries = sorted(
|
||||
by_days[delta],
|
||||
key=lambda e: (e.as_date, (e.title or e.name or "")),
|
||||
)
|
||||
|
||||
body = "\n".join(body_lines)
|
||||
for ev in entries:
|
||||
d = ev.as_date
|
||||
# Portable UK-style date: weekday, D Month YYYY
|
||||
date_str = f"{d:%A}, {d.day} {d:%B %Y}"
|
||||
label = ev.title or ev.name
|
||||
lines.append(f" • {label} — {date_str}")
|
||||
|
||||
if sys.stdin.isatty():
|
||||
print(f"Birthday reminder: {subject}")
|
||||
print(body)
|
||||
lines.append("")
|
||||
|
||||
agenda.mail.send_mail(config, subject, body)
|
||||
lines.append("View all birthdays: https://edwardbetts.com/agenda/birthdays")
|
||||
body = "\n".join(lines)
|
||||
|
||||
if sys.stdin.isatty():
|
||||
print(f"Birthday reminder: {subject}\n{body}")
|
||||
|
||||
agenda.mail.send_mail(config, subject, body)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
Loading…
Reference in a new issue