From 34860e8ae4db9e9568b3e13f12fba611ad903169 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Fri, 7 Aug 2026 13:34:18 +0200 Subject: [PATCH] birthday unknown year filter --- src/api/main.py | 3 +++ src/api/templates/contact.html | 2 +- src/api/templates/dashboard.html | 2 +- src/api/templates/index.html | 2 +- src/mailer.py | 15 +++++++++------ src/utils.py | 22 ++++++++++++++++++++++ 6 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 src/utils.py diff --git a/src/api/main.py b/src/api/main.py index 4e861c1..0d96293 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -24,6 +24,7 @@ from api.auth import get_current_user, resolve_account_for_user from api.schemas import ContactListResponse, ContactOut, SyncRunOut from config import Config from mailer import build_message, fetch_birthdays_for_date, send_message +from utils import fmt_birthday_age, fmt_birthday_short logging.basicConfig(level=Config.LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger("api") @@ -33,6 +34,8 @@ app.add_middleware(SessionMiddleware, secret_key=secrets.token_hex(32), session_ app.mount("/static", StaticFiles(directory="api/static"), name="static") templates = Jinja2Templates(directory="api/templates") templates.env.filters["urlquote"] = lambda s: quote_plus(s or "") +templates.env.filters["fmt_birthday"] = fmt_birthday_short +templates.env.filters["fmt_age"] = fmt_birthday_age def _fmt_ts(dt) -> str | None: diff --git a/src/api/templates/contact.html b/src/api/templates/contact.html index 578df63..da6646f 100644 --- a/src/api/templates/contact.html +++ b/src/api/templates/contact.html @@ -332,7 +332,7 @@ {% if contact.birthday %}
- 馃巶 {{ contact.birthday }} + 馃巶 {{ contact.birthday|fmt_birthday }}
{% endif %} diff --git a/src/api/templates/dashboard.html b/src/api/templates/dashboard.html index 9240f13..c1061a3 100644 --- a/src/api/templates/dashboard.html +++ b/src/api/templates/dashboard.html @@ -367,7 +367,7 @@
{{ b.organization }}
{% endif %}
- {{ b.birthday.strftime('%d.%m.') }}{% if b.birthday.year and b.birthday.year < current_year %} 路 {{ current_year - b.birthday.year }} Jahre{% endif %} + {{ b.birthday|fmt_birthday }}{% if b.birthday|fmt_age %} 路 {{ b.birthday|fmt_age(current_year) }} Jahre{% endif %}
{% if b.photo_url %} diff --git a/src/api/templates/index.html b/src/api/templates/index.html index 2e2ad1c..4c89376 100644 --- a/src/api/templates/index.html +++ b/src/api/templates/index.html @@ -198,7 +198,7 @@
{{ c.organization }}
{% endif %} {% if c.birthday %} -
{{ c.birthday }}
+
{{ c.birthday|fmt_birthday }}
{% endif %}
diff --git a/src/mailer.py b/src/mailer.py index b79181f..11044b8 100644 --- a/src/mailer.py +++ b/src/mailer.py @@ -13,6 +13,7 @@ from email.message import EmailMessage import db from config import Config +from utils import fmt_birthday_age, fmt_birthday_short logging.basicConfig(level=Config.LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger("mailer") @@ -68,9 +69,10 @@ def build_message(birthdays: list[dict], target_date: date | None = None) -> Ema plain_lines = [f"Heutige Geburtstage ({today.isoformat()}):", ""] for b in birthdays: - age = today.year - b["birthday"].year - date_str = b["birthday"].strftime("%d.%m.") - line = f"- {b['full_name']} 路 {date_str} 路 {age} Jahre" + date_str = fmt_birthday_short(b["birthday"]) + age = fmt_birthday_age(b["birthday"], today) + age_str = f" 路 {age} Jahre" if age is not None else "" + line = f"- {b['full_name']} 路 {date_str}{age_str}" if b.get("organization"): line += f" ({b['organization']})" if Config.WEB_URL: @@ -82,8 +84,9 @@ def build_message(birthdays: list[dict], target_date: date | None = None) -> Ema cards_html = "" for b in birthdays: - age = today.year - b["birthday"].year - date_str = b["birthday"].strftime("%d.%m.") + date_str = fmt_birthday_short(b["birthday"]) + age = fmt_birthday_age(b["birthday"], today) + age_str = f" 路 {age} Jahre" if age is not None else "" contact_link = f"{Config.WEB_URL.rstrip('/')}/contacts/{b['id']}" if Config.WEB_URL else "" name_html = f'{b["full_name"]}' if contact_link else b["full_name"] is_today = b["birthday"].month == today.month and b["birthday"].day == today.day @@ -99,7 +102,7 @@ def build_message(birthdays: list[dict], target_date: date | None = None) -> Ema
{name_html}
{org_html} -
馃巶 {date_str} 路 {age} Jahre
+
馃巶 {date_str}{age_str}
{photo_html}
diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..0a6c2db --- /dev/null +++ b/src/utils.py @@ -0,0 +1,22 @@ +# Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de) +# Licensed under the MIT License. See LICENSE file in project root for details. +from datetime import date + +UNKNOWN_YEARS = frozenset({0, 1604, 1900}) + + +def is_unknown_year(birthday: date) -> bool: + return birthday.year in UNKNOWN_YEARS + + +def fmt_birthday_short(birthday: date) -> str: + if is_unknown_year(birthday): + return birthday.strftime("%d.%m.") + return birthday.strftime("%d.%m.%Y") + + +def fmt_birthday_age(birthday: date, reference) -> int | None: + if is_unknown_year(birthday): + return None + ref_year = reference if isinstance(reference, int) else reference.year + return ref_year - birthday.year