Coverage for src/couchers/templates/v2.py: 98%
63 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-20 18:03 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-20 18:03 +0000
1"""
2template mailer/push notification formatter v2
3"""
5import logging
6from datetime import date
7from html import escape
8from pathlib import Path
9from zoneinfo import ZoneInfo
11import phonenumbers
12from jinja2 import Environment, FileSystemLoader
14from couchers import urls
15from couchers.config import config
16from couchers.email import queue_email
17from couchers.utils import get_tz_as_text, now, to_aware_datetime
19logger = logging.getLogger(__name__)
21template_folder = Path(__file__).parent / ".." / ".." / ".." / "templates" / "v2"
23loader = FileSystemLoader(template_folder)
24env = Environment(loader=loader, trim_blocks=True)
27def v2esc(value):
28 return escape(str(value))
31def v2multiline(value):
32 return "<br />".join(value.splitlines())
35def v2sf(value):
36 return value
39def v2url(value):
40 return value
43def v2phone(value):
44 return phonenumbers.format_number(phonenumbers.parse(value), phonenumbers.PhoneNumberFormat.INTERNATIONAL)
47def v2date(value, user):
48 # todo: user locale-based date formatting
49 if isinstance(value, str):
50 value = date.fromisoformat(value)
51 return value.strftime("%A %-d %B %Y")
54def v2time(value, user):
55 tz = ZoneInfo(user.timezone or "Etc/UTC")
56 return value.astimezone(tz=tz).strftime("%-I:%M %p (%H:%M)")
59def v2timestamp(value, user):
60 tz = ZoneInfo(user.timezone or "Etc/UTC")
61 return to_aware_datetime(value).astimezone(tz=tz).strftime("%A %-d %B %Y at %-I:%M %p (%H:%M)")
64def v2avatar(user):
65 if not user.avatar_thumbnail_url:
66 return urls.icon_url()
67 return user.avatar_thumbnail_url
70def v2quote(value):
71 """
72 Multiline quote
73 """
74 return "\n> ".join([""] + value.splitlines())
77def add_filters(env):
78 env.filters["v2esc"] = v2esc
79 env.filters["v2multiline"] = v2multiline
80 env.filters["v2sf"] = v2sf
81 env.filters["v2url"] = v2url
82 env.filters["v2phone"] = v2phone
83 env.filters["v2date"] = v2date
84 env.filters["v2time"] = v2time
85 env.filters["v2timestamp"] = v2timestamp
86 env.filters["v2avatar"] = v2avatar
87 env.filters["v2quote"] = v2quote
90add_filters(env)
93def send_simple_pretty_email(session, recipient, subject, template_name, template_args):
94 """
95 This is a simplified version of couchers.notifications.background._send_email_notification
97 It's for the few security emails where we don't have a user to email but send directly to an email address.
98 """
99 template_args["_year"] = now().year
100 template_args["_timezone_display"] = get_tz_as_text("Etc/UTC")
102 plain_unsub_section = "\n\n---\n\nThis is a security email, you cannot unsubscribe from it."
103 html_unsub_section = "This is a security email, you cannot unsubscribe from it."
105 plain_tmplt = (template_folder / f"{template_name}.txt").read_text()
106 plain = env.from_string(plain_tmplt + plain_unsub_section).render(template_args)
107 html_tmplt = (template_folder / "generated_html" / f"{template_name}.html").read_text()
108 html = env.from_string(html_tmplt.replace("___UNSUB_SECTION___", html_unsub_section)).render(template_args)
110 queue_email(
111 session,
112 sender_name=config["NOTIFICATION_EMAIL_SENDER"],
113 sender_email=config["NOTIFICATION_EMAIL_ADDRESS"],
114 recipient=recipient,
115 subject=config["NOTIFICATION_PREFIX"] + subject,
116 plain=plain,
117 html=html,
118 source_data=config["VERSION"] + f"/{template_name}",
119 )