diff --git a/mylib/email.py b/mylib/email.py
index 1cca112..e90f1d7 100644
--- a/mylib/email.py
+++ b/mylib/email.py
@@ -46,15 +46,18 @@ class EmailClient(
"encoding": "utf-8",
"catch_all_addr": None,
"just_try": False,
+ "templates_path": None,
}
templates = {}
- def __init__(self, templates=None, **kwargs):
+ def __init__(self, templates=None, initialize=False, **kwargs):
super().__init__(**kwargs)
assert templates is None or isinstance(templates, dict)
self.templates = templates if templates else {}
+ if initialize:
+ self.initialize()
# pylint: disable=arguments-differ,arguments-renamed
def configure(self, use_smtp=True, just_try=True, **kwargs):
@@ -137,8 +140,40 @@ class EmailClient(
comment="Just-try mode: do not really send emails",
)
+ section.add_option(
+ StringOption,
+ "templates_path",
+ comment="Path to templates directory",
+ )
+
return section
+ def initialize(self, *args, **kwargs): # pylint: disable=arguments-differ
+ """Configuration initialized hook"""
+ super().initialize(*args, **kwargs)
+ self.load_templates_directory()
+
+ def load_templates_directory(self, templates_path=None):
+ """Load templates from specified directory"""
+ if templates_path is None:
+ templates_path = self._get_option("templates_path")
+ if not templates_path:
+ return
+ log.debug("Load email templates from %s directory", templates_path)
+ for filename in os.listdir(templates_path):
+ filepath = os.path.join(templates_path, filename)
+ if not os.path.isfile(filepath):
+ continue
+ template_name, template_type = os.path.splitext(filename)
+ if template_type not in [".html", ".txt", ".subject"]:
+ continue
+ template_type = "text" if template_type == ".txt" else template_type[1:]
+ if template_name not in self.templates:
+ self.templates[template_name] = {}
+ log.debug("Load email template %s %s from %s", template_name, template_type, filepath)
+ with open(filepath, encoding="utf8") as file_desc:
+ self.templates[template_name][template_type] = MakoTemplate(file_desc.read())
+
def forge_message(
self,
rcpt_to,
@@ -179,7 +214,11 @@ class EmailClient(
)
)
if subject:
- msg["Subject"] = subject.format(**template_vars)
+ msg["Subject"] = (
+ subject.render(**template_vars)
+ if isinstance(subject, MakoTemplate)
+ else subject.format(**template_vars)
+ )
msg["Date"] = email.utils.formatdate(None, True)
encoding = encoding if encoding else self._get_option("encoding")
if template:
@@ -189,7 +228,11 @@ class EmailClient(
assert self.templates[template].get(
"subject"
), f"No subject defined in template {template}"
- msg["Subject"] = self.templates[template]["subject"].format(**template_vars)
+ msg["Subject"] = (
+ self.templates[template]["subject"].render(**template_vars)
+ if isinstance(self.templates[template]["subject"], MakoTemplate)
+ else self.templates[template]["subject"].format(**template_vars)
+ )
# Put HTML part in last one to prefered it
parts = []
diff --git a/mylib/scripts/email_templates/test.html b/mylib/scripts/email_templates/test.html
new file mode 100644
index 0000000..f9ba261
--- /dev/null
+++ b/mylib/scripts/email_templates/test.html
@@ -0,0 +1 @@
+Just a test email. (sent at ${sent_date})
diff --git a/mylib/scripts/email_templates/test.subject b/mylib/scripts/email_templates/test.subject
new file mode 100644
index 0000000..4e9552a
--- /dev/null
+++ b/mylib/scripts/email_templates/test.subject
@@ -0,0 +1 @@
+Test email
diff --git a/mylib/scripts/email_templates/test.txt b/mylib/scripts/email_templates/test.txt
new file mode 100644
index 0000000..b48fd30
--- /dev/null
+++ b/mylib/scripts/email_templates/test.txt
@@ -0,0 +1 @@
+Just a test email sent at ${sent_date}.
diff --git a/mylib/scripts/email_test.py b/mylib/scripts/email_test.py
index 44bef4f..542170a 100644
--- a/mylib/scripts/email_test.py
+++ b/mylib/scripts/email_test.py
@@ -2,10 +2,9 @@
import datetime
import getpass
import logging
+import os
import sys
-from mako.template import Template as MakoTemplate
-
from mylib.scripts.helpers import add_email_opts, get_opts_parser, init_email_client, init_logging
log = logging.getLogger("mylib.scripts.email_test")
@@ -18,7 +17,10 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
# Options parser
parser = get_opts_parser(just_try=True)
- add_email_opts(parser)
+ add_email_opts(
+ parser,
+ templates_path=os.path.join(os.path.dirname(os.path.realpath(__file__)), "email_templates"),
+ )
test_opts = parser.add_argument_group("Test email options")
@@ -31,6 +33,15 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
help="Test email recipient",
)
+ test_opts.add_argument(
+ "-T",
+ "--template",
+ action="store_true",
+ dest="template",
+ help="Template name to send (default: test)",
+ default="test",
+ )
+
test_opts.add_argument(
"-m",
"--mako",
@@ -51,26 +62,7 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
if options.email_smtp_user and not options.email_smtp_password:
options.email_smtp_password = getpass.getpass("Please enter SMTP password: ")
- email_client = init_email_client(
- options,
- templates=dict(
- test=dict(
- subject="Test email",
- text=(
- "Just a test email sent at {sent_date}."
- if not options.test_mako
- else MakoTemplate("Just a test email sent at ${sent_date}.")
- ),
- html=(
- "Just a test email. (sent at {sent_date})"
- if not options.test_mako
- else MakoTemplate(
- "Just a test email. (sent at ${sent_date})"
- )
- ),
- )
- ),
- )
+ email_client = init_email_client(options)
log.info("Send a test email to %s", options.test_to)
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
diff --git a/mylib/scripts/email_test_with_config.py b/mylib/scripts/email_test_with_config.py
index 9e966d3..b20b336 100644
--- a/mylib/scripts/email_test_with_config.py
+++ b/mylib/scripts/email_test_with_config.py
@@ -1,10 +1,9 @@
""" Test Email client using mylib.config.Config for configuration """
import datetime
import logging
+import os
import sys
-from mako.template import Template as MakoTemplate
-
from mylib.config import Config
from mylib.email import EmailClient
@@ -20,6 +19,11 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
email_client = EmailClient(config=config)
email_client.configure()
+ config.set_default(
+ "email",
+ "templates_path",
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), "email_templates"),
+ )
# Options parser
parser = config.get_arguments_parser(description=__doc__)
@@ -35,6 +39,15 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
help="Test email recipient",
)
+ test_opts.add_argument(
+ "-T",
+ "--template",
+ action="store_true",
+ dest="template",
+ help="Template name to send (default: test)",
+ default="test",
+ )
+
test_opts.add_argument(
"-m",
"--mako",
@@ -49,24 +62,6 @@ def main(argv=None): # pylint: disable=too-many-locals,too-many-statements
parser.error("You must specify test email recipient using -t/--to parameter")
sys.exit(1)
- email_client.templates = dict(
- test=dict(
- subject="Test email",
- text=(
- "Just a test email sent at {sent_date}."
- if not options.test_mako
- else MakoTemplate("Just a test email sent at ${sent_date}.")
- ),
- html=(
- "Just a test email. (sent at {sent_date})"
- if not options.test_mako
- else MakoTemplate(
- "Just a test email. (sent at ${sent_date})"
- )
- ),
- )
- )
-
logging.info("Send a test email to %s", options.test_to)
if email_client.send(options.test_to, template="test", sent_date=datetime.datetime.now()):
logging.info("Test email sent")
diff --git a/mylib/scripts/helpers.py b/mylib/scripts/helpers.py
index e12a00d..e02db2e 100644
--- a/mylib/scripts/helpers.py
+++ b/mylib/scripts/helpers.py
@@ -87,7 +87,7 @@ def get_opts_parser(desc=None, just_try=False, just_one=False, progress=False, c
return parser
-def add_email_opts(parser, config=None):
+def add_email_opts(parser, config=None, **defaults):
"""Add email options"""
email_opts = parser.add_argument_group("Email options")
@@ -103,7 +103,9 @@ def add_email_opts(parser, config=None):
sender_name=getpass.getuser(),
sender_email=f"{getpass.getuser()}@{socket.gethostname()}",
catch_all=False,
+ templates_path=None,
)
+ default_config.update(defaults)
email_opts.add_argument(
"--smtp-host",
@@ -220,6 +222,18 @@ def add_email_opts(parser, config=None):
default=get_default_opt_value(config, default_config, "catch_all"),
)
+ email_opts.add_argument(
+ "--templates-path",
+ action="store",
+ type=str,
+ dest="email_templates_path",
+ help=(
+ "Load templates from specify directory "
+ f'(default: {get_default_opt_value(config, default_config, "templates_path")})'
+ ),
+ default=get_default_opt_value(config, default_config, "templates_path"),
+ )
+
def init_email_client(options, **kwargs):
"""Initialize email client from calling script options"""
@@ -239,6 +253,8 @@ def init_email_client(options, **kwargs):
catch_all_addr=options.email_catch_all,
just_try=options.just_try if hasattr(options, "just_try") else False,
encoding=options.email_encoding,
+ templates_path=options.email_templates_path,
+ initialize=True,
**kwargs,
)