Skip to content

Commit

Permalink
feat(mailer): Use colors and logo in email templates
Browse files Browse the repository at this point in the history
  • Loading branch information
MarekSuchanek committed Jan 14, 2024
1 parent b7f8bb6 commit 22686d8
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
4 changes: 4 additions & 0 deletions packages/dsw-mailer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Enable to use colors and logo in email templates


## [4.2.1]

Expand Down
10 changes: 7 additions & 3 deletions packages/dsw-mailer/dsw/mailer/mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,21 @@ def work(self, cmd: PersistentCommand):
msg_id=cmd.uuid,
trigger='PersistentComment',
)
# get tenant config from DB
tenant_cfg = app_ctx.db.get_tenant_config(tenant_uuid=cmd.tenant_uuid)
LOG.debug(f'Tenant config from DB: {tenant_cfg}')
rq.style.from_dict(tenant_cfg.look_and_feel)
# get mailer config from DB
cfg = _transform_mail_config(
mail_cfg = _transform_mail_config(
cfg=app_ctx.db.get_mail_config(tenant_uuid=cmd.tenant_uuid),
)
LOG.debug(f'Config from DB: {cfg}')
LOG.debug(f'Mail config from DB: {mail_cfg}')
# client URL
rq.client_url = cmd.body.get('clientUrl', app_ctx.cfg.general.client_url)
rq.domain = urllib.parse.urlparse(rq.client_url).hostname
# update Sentry info
SentryReporter.set_context('template', rq.template_name)
self.send(rq, cfg)
self.send(rq, mail_cfg)
SentryReporter.set_context('template', '-')
SentryReporter.set_context('cmd_uuid', '-')
Context.get().update_trace_id('-')
Expand Down
85 changes: 84 additions & 1 deletion packages/dsw-mailer/dsw/mailer/model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,82 @@
from typing import Optional


class Color:

def __init__(self, color_hex: str = '#000000'):
h = color_hex.lstrip('#')
self.red, self.green, self.blue = tuple(int(h[i:i+2], 16) for i in (0, 2, 4))

@property
def hex(self):
return f'#{self.red:02x}{self.green:02x}{self.blue:02x}'

@property
def luminance(self):
# https://www.w3.org/WAI/GL/wiki/Relative_luminance

def _luminance_component(component: int):
c = component / 255
if c <= 0.03928:
return c / 12.92
else:
return ((c + 0.055) / 1.055) ** 2.4

r = _luminance_component(self.red)
g = _luminance_component(self.green)
b = _luminance_component(self.blue)
return 0.2126 * r + 0.7152 * g + 0.0722 * b

@property
def is_dark(self):
return self.luminance < 0.5

@property
def is_light(self):
return not self.is_dark

@property
def contrast_color(self) -> 'Color':
return Color('#ffffff') if self.is_dark else Color('#000000')

def __str__(self):
return self.hex


class StyleConfig:
_DEFAULT = None

def __init__(self, logo_url: Optional[str], primary_color: str,
illustration_color: str):
self.logo_url = logo_url
self.primary_color = Color(primary_color)
self.illustration_color = Color(illustration_color)

def from_dict(self, data: dict):
if data.get('logoUrl', None) is not None:
self.logo_url = data.get('logoUrl')
if data.get('primaryColor', None) is not None:
self.primary_color = Color(data.get(
'primaryColor',
self.default().primary_color.hex
))
if data.get('illustrationColor', None) is not None:
self.illustration_color = Color(data.get(
'illustrationColor',
self.default().illustration_color.hex,
))

@classmethod
def default(cls):
if cls._DEFAULT is None:
cls._DEFAULT = StyleConfig(
logo_url=None,
primary_color='#0033aa',
illustration_color='#4285f4',
)
return cls._DEFAULT


class TemplateDescriptorPart:

DEFAULTS = {
Expand Down Expand Up @@ -85,14 +161,16 @@ def load_from_file(data: dict) -> 'TemplateDescriptor':
class MessageRequest:

def __init__(self, message_id: str, template_name: str, trigger: str,
ctx: dict, recipients: list[str]):
ctx: dict, recipients: list[str], style: Optional[StyleConfig] = None):
self.id = message_id
self.template_name = template_name
self.trigger = trigger
self.ctx = ctx
self.recipients = recipients
self.domain = None # type: Optional[str]
self.client_url = '' # type: str
self.style = style or StyleConfig.default()
self.ctx['style'] = self.style

@staticmethod
def load_from_file(data: dict) -> 'MessageRequest':
Expand All @@ -102,6 +180,11 @@ def load_from_file(data: dict) -> 'MessageRequest':
trigger=data.get('trigger', 'input_file'),
ctx=data.get('ctx', {}),
recipients=data.get('recipients', []),
style=StyleConfig(
logo_url=data.get('styleLogoUrl', None),
primary_color=data.get('stylePrimaryColor', '#019AD6'),
illustration_color=data.get('styleIllustrationColor', '#019AD6'),
),
)


Expand Down
3 changes: 2 additions & 1 deletion packages/dsw-mailer/dsw/mailer/templates.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import datetime
import dateutil.parser
import jinja2
import jinja2.sandbox
import json
import logging
import pathlib
Expand Down Expand Up @@ -65,7 +66,7 @@ class TemplateRegistry:
def __init__(self, cfg: MailerConfig, workdir: pathlib.Path):
self.cfg = cfg
self.workdir = workdir
self.j2_env = jinja2.Environment(
self.j2_env = jinja2.sandbox.SandboxedEnvironment(
loader=jinja2.FileSystemLoader(searchpath=workdir),
extensions=['jinja2.ext.do'],
)
Expand Down

0 comments on commit 22686d8

Please sign in to comment.