Celery + RabbitMQ + Jinja + .env do wysyłki maili.

Minimalny, działający przykład, który można od razu uruchomić na wielu workerach.

celery_mail_demo/
├── celery_app.py
├── tasks.py
├── templates/
│   └── mail/
│       └── report.html
├── .env
├── requirements.txt
└── run_worker.sh

requirements.txt

celery==5.3.1
jinja2
python-dotenv

.env

# RabbitMQ
BROKER_URL=amqp://celery:secret@192.168.0.150:5672/analysis

# SMTP
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@example.com
SMTP_PASSWORD=SECRET
SMTP_FROM=noreply@example.com

celery_app.py

import os
from celery import Celery
from dotenv import load_dotenv

load_dotenv()  # wczytuje .env

celery = Celery(
    "mail_demo",
    broker=os.environ["BROKER_URL"],
    backend="rpc://"
)

tasks.py

import os
from celery_app import celery
from jinja2 import Environment, FileSystemLoader
from email.message import EmailMessage
import smtplib
from datetime import datetime

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

jinja_env = Environment(
    loader=FileSystemLoader(os.path.join(BASE_DIR, "templates"))
)

def _send_smtp(msg):
    with smtplib.SMTP(os.environ["SMTP_HOST"], int(os.environ["SMTP_PORT"]), timeout=30) as smtp:
        smtp.starttls()
        smtp.login(os.environ["SMTP_USER"], os.environ["SMTP_PASSWORD"])
        smtp.send_message(msg)

@celery.task(bind=True, autoretry_for=(Exception,), retry_backoff=30, retry_kwargs={"max_retries": 3})
def send_report_mail(self, to, station, start, end, results):
    template = jinja_env.get_template("mail/report.html")
    html = template.render(
        station=station,
        start=start,
        end=end,
        results=results,
        generated_at=datetime.utcnow().isoformat()
    )

    msg = EmailMessage()
    msg["From"] = os.environ["SMTP_FROM"]
    msg["To"] = to
    msg["Subject"] = f"Raport {station}"

    msg.set_content("Ten email zawiera raport HTML.")
    msg.add_alternative(html, subtype="html")

    _send_smtp(msg)

Szablon Jinja – templates/mail/report.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <h2>Raport dla stacji {{ station }}</h2>
    <p>Zakres: {{ start }} – {{ end }}</p>

    <ul>
        {% for key, value in results.items() %}
            <li><b>{{ key }}</b>: {{ value }}</li>
        {% endfor %}
    </ul>

    <p style="color: gray; font-size: 12px">Wygenerowano: {{ generated_at }}</p>
</body>
</html>

Skrypt uruchamiający worker – run_worker.sh

#!/bin/bash
source venv/bin/activate
export $(cat .env | xargs)  # wczytuje zmienne środowiskowe z .env
celery -A celery_app.celery worker --loglevel=info --concurrency=2

Co MUSI być na każdym workerze

  1. Ten sam kod: celery_app.py + tasks.py
  2. Szablony Jinja: templates/mail/report.html
  3. .env (lub ENV w systemd)
  4. Python + venv + dependencies
  5. Dostęp do SMTP i RabbitMQ

Uruchomienie workera

pip install -r requirements.txt

chmod +x run_worker.sh

./run_worker.sh

Przykład wywołania zadania z Python (CLI)

from tasks import send_report_mail

send_report_mail.delay(
    to="user@example.com",
    station="ST001",
    start="2025-12-01",
    end="2025-12-29",
    results={"temp_avg": 23, "humidity": 70}
)