60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
import os
|
|
import logging
|
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
from repositories.brief_repository import brief_repository
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
RESEND_API_KEY = os.getenv('RESEND_API_KEY')
|
|
TEMPLATES_DIR = os.path.join(os.path.dirname(__file__), '..', 'templates', 'emails')
|
|
|
|
# Plain Jinja2 (not flask.render_template) so this service has no
|
|
# dependency on an active Flask request/app context — it must also run
|
|
# from the n8n-triggered Flask route AND, in principle, from an RQ
|
|
# worker process. Autoescaping is mandatory: brief content includes
|
|
# scraped third-party trip names which must never be interpreted as
|
|
# HTML in the rendered email.
|
|
_jinja_env = Environment(
|
|
loader=FileSystemLoader(TEMPLATES_DIR),
|
|
autoescape=select_autoescape(['html'])
|
|
)
|
|
|
|
|
|
def send_weekly_brief(tenant_id):
|
|
"""
|
|
Renders the weekly intelligence brief as branded HTML and sends it
|
|
via Resend to the tenant admin email.
|
|
|
|
Data flow:
|
|
tenant_id → brief_repository.get_weekly_data() (price_history,
|
|
new products, battlecard updates for the past 7 days) →
|
|
brief.html Jinja2 template rendered → Resend API →
|
|
digest_logs record created (sent or failed)
|
|
"""
|
|
data = brief_repository.get_weekly_data(tenant_id)
|
|
admin_email = data.get('admin_email')
|
|
if not admin_email:
|
|
logger.error(f'No admin_email on file for tenant {tenant_id} — skipping weekly brief')
|
|
return
|
|
|
|
template = _jinja_env.get_template('brief.html')
|
|
html = template.render(**data)
|
|
|
|
try:
|
|
# Imported lazily so this module is importable without the
|
|
# `resend` package configured in test environments.
|
|
import resend
|
|
resend.api_key = RESEND_API_KEY
|
|
resend.Emails.send({
|
|
'from': 'Bettersight <brief@bettersight.io>',
|
|
'to': admin_email,
|
|
'subject': f"Bettersight Weekly Brief — Week of {data['week_of']}",
|
|
'html': html,
|
|
})
|
|
brief_repository.log_digest(tenant_id, 'weekly', recipient_email=admin_email, status='sent')
|
|
except Exception as e:
|
|
# A failed email send is a side-effect failure — it must not
|
|
# raise into the n8n workflow that triggered this run.
|
|
logger.error(f'Weekly brief send failed for tenant {tenant_id}: {str(e)}')
|
|
brief_repository.log_digest(tenant_id, 'weekly', recipient_email=admin_email, status='failed')
|