Coverage for app / backend / src / couchers / email / dump_emails.py: 0%
48 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-24 17:34 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-24 17:34 +0000
1"""
2Dumps emails subjects and html/plaintext bodies with dummy data.
3"""
5import inspect
6import re
7import sys
8from argparse import ArgumentParser
9from dataclasses import dataclass
10from pathlib import Path
11from zoneinfo import ZoneInfo
13import couchers.email.emails
14from couchers.email.emails import EmailBase
15from couchers.email.rendering import (
16 EmailFooter,
17 UnsubscribeInfo,
18 UnsubscribeLink,
19 render_html_body,
20 render_plaintext_body,
21)
22from couchers.i18n import LocalizationContext
23from couchers.templating import template_folder
26@dataclass
27class CommandLineArgs:
28 filter: str
29 outdir: Path
30 locale: str
32 @staticmethod
33 def parse(args: list[str]) -> CommandLineArgs:
34 parser = ArgumentParser(description=__doc__)
35 parser.add_argument("--filter", type=str, default="*", help="A filter for email classes to dump.")
36 parser.add_argument(
37 "--outdir", type=Path, default=template_folder, help="The directory to write email bodies to."
38 )
39 parser.add_argument("--locale", type=str, default="en", help="The locale to use.")
40 parsed_args = parser.parse_args(args)
41 return CommandLineArgs(**parsed_args.__dict__)
44def main() -> None:
45 args = CommandLineArgs.parse(sys.argv[1:])
46 loc_context = LocalizationContext(locale=args.locale, timezone=ZoneInfo("Etc/UTC"))
48 footer = EmailFooter(
49 unsubscribe_info=UnsubscribeInfo(
50 manage_notifications_url="https://example.com/manage-notifications",
51 do_not_email_url="https://example.com/do-not-email",
52 topic_action_link=UnsubscribeLink(text="topic-action", url="https://example.com/unsubscribe"),
53 )
54 )
56 filter_regex = re.compile(re.escape(args.filter).replace(r"\*", ".*?"))
58 for _, klass in inspect.getmembers(couchers.email.emails, lambda o: inspect.isclass(o) and o.__base__ == EmailBase):
59 email_class: type[EmailBase] = klass
60 if filter_regex.fullmatch(email_class.__name__):
61 dump_email(email_class.dummy_data(), footer, loc_context, args.outdir)
64def dump_email(email: EmailBase, footer: EmailFooter, loc_context: LocalizationContext, outdir: Path) -> None:
65 """Dumps an email's subject and plaintext+html body to a file."""
66 subject_line = email.get_subject_line(loc_context)
67 preview_line = email.get_preview_line(loc_context)
68 blocks = email.get_body_blocks(loc_context)
70 outdir.mkdir(exist_ok=True)
72 print(f"Dumping email class {email.__class__.__name__}")
73 print(f" Subject: {subject_line}")
75 html_path = outdir / f"{email.__class__.__name__}.html"
76 print(f" Rendering html to {html_path}...")
77 html = render_html_body(
78 subject=subject_line, preview=preview_line, blocks=blocks, footer=footer, loc_context=loc_context
79 )
80 html_path.write_text(html)
82 plaintext_path = outdir / f"{email.__class__.__name__}.txt"
83 print(f" Rendering plaintext to {plaintext_path}...")
84 plaintext = render_plaintext_body(blocks=blocks, footer=footer, loc_context=loc_context)
85 plaintext_path.write_text(plaintext)
88if __name__ == "__main__":
89 main()