[WIP] working on mailing templates
rodzic
70991fc2ea
commit
1326b13948
|
@ -26,6 +26,7 @@ BASE_DIR = os.path.dirname(PROJECT_DIR)
|
|||
INSTALLED_APPS = [
|
||||
"home",
|
||||
"store",
|
||||
"mailings",
|
||||
"blog",
|
||||
"search",
|
||||
"wagtail.contrib.forms",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MailingsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'mailings'
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 4.1.9 on 2023-06-21 18:32
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="MailTemplate",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("template_name", models.CharField(max_length=255, unique=True)),
|
||||
("template", models.FileField(upload_to="mail_templates")),
|
||||
("subject", models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="OutgoingEmail",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("sender", models.EmailField(max_length=254)),
|
||||
("recipient", models.EmailField(max_length=254)),
|
||||
("sent", models.BooleanField(default=False)),
|
||||
(
|
||||
"template",
|
||||
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="mailings.mailtemplate"),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,94 @@
|
|||
from typing import Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
from django.db import models
|
||||
from django.db import transaction
|
||||
from django.template import (
|
||||
Template,
|
||||
Context
|
||||
)
|
||||
from django.core.mail import EmailMessage
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
@dataclass
|
||||
class Attachment:
|
||||
name: str
|
||||
content: Any
|
||||
contenttype: str
|
||||
|
||||
|
||||
def send_mail(
|
||||
to: list[str],
|
||||
attachments: list[Attachment],
|
||||
subject: str,
|
||||
body: str,
|
||||
sender_email: str = settings.DEFAULT_FROM_EMAIL
|
||||
):
|
||||
message = EmailMessage(
|
||||
subject=subject,
|
||||
body=body,
|
||||
from_email=sender_email,
|
||||
to=to
|
||||
)
|
||||
for attachment in attachments:
|
||||
message.attach(attachment.name, attachment.content, attachment.contenttype)
|
||||
return bool(message.send())
|
||||
|
||||
|
||||
class MailTemplate(models.Model):
|
||||
template_name = models.CharField(max_length=255, unique=True)
|
||||
|
||||
template = models.FileField(
|
||||
upload_to="mail_templates"
|
||||
)
|
||||
subject = models.CharField(max_length=255)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
# delete file
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
@transaction.on_commit
|
||||
def remove_template_file(self):
|
||||
self.template.delete()
|
||||
|
||||
def load_and_process_template(self, context: dict|Context):
|
||||
if not self.template:
|
||||
raise FileNotFoundError("Template file is missing")
|
||||
if isinstance(context, dict):
|
||||
context = Context(context)
|
||||
with open(self.template.path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
template = Template(content)
|
||||
return template.render(context)
|
||||
|
||||
|
||||
class OutgoingEmailManager(models.Manager):
|
||||
|
||||
def send(
|
||||
self, template_name: str,
|
||||
recipient: str, context: dict | Context,
|
||||
sender:str, attachments: list[Attachment] = None
|
||||
):
|
||||
template = MailTemplate.objects.get(template_name=template_name)
|
||||
outgoing_email = self.create(template=template, recipient=recipient)
|
||||
attachments = attachments or []
|
||||
# send email
|
||||
sent = send_mail(
|
||||
to=[recipient], sender_email=sender,
|
||||
subject=template.subject, content=template.load_and_process_template(context),
|
||||
attachments=attachments
|
||||
)
|
||||
outgoing_email.sent = sent
|
||||
outgoing_email.save()
|
||||
return outgoing_email
|
||||
|
||||
|
||||
class OutgoingEmail(models.Model):
|
||||
template = models.ForeignKey(MailTemplate, on_delete=models.CASCADE)
|
||||
sender = models.EmailField()
|
||||
recipient = models.EmailField()
|
||||
|
||||
sent = models.BooleanField(default=False)
|
||||
|
||||
objects = OutgoingEmailManager()
|
|
@ -0,0 +1,64 @@
|
|||
from django.test import TestCase
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
from mailings.models import (
|
||||
MailTemplate,
|
||||
OutgoingEmail,
|
||||
)
|
||||
|
||||
|
||||
class TestMailTemplate(TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.mail_template = MailTemplate.objects.create(
|
||||
template_name="test_template",
|
||||
template=SimpleUploadedFile(
|
||||
"test_template.html", b"<html>{{test_var}}</html>"
|
||||
),
|
||||
subject="Test subject",
|
||||
)
|
||||
|
||||
def test_load_and_process_template_success(self):
|
||||
content = self.mail_template.load_and_process_template({"test_var": "test"})
|
||||
self.assertEqual(content, "<html>test</html>")
|
||||
|
||||
def test_load_and_process_template_missing_var_failure(self):
|
||||
content = self.mail_template.load_and_process_template({})
|
||||
self.assertEqual(content, "<html></html>")
|
||||
|
||||
def test_load_and_preprocess_template_no_template_file(self):
|
||||
self.mail_template.template.delete()
|
||||
self.mail_template.template = None
|
||||
self.mail_template.save()
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
self.mail_template.load_and_process_template({})
|
||||
|
||||
|
||||
class TestOutgoingEmail(TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.mail_template = MailTemplate.objects.create(
|
||||
template_name="test_template",
|
||||
template=SimpleUploadedFile(
|
||||
"test_template.html", b"<html>{{test_var}}</html>"
|
||||
),
|
||||
subject="Test subject",
|
||||
)
|
||||
|
||||
def test_send_success(self):
|
||||
mail = OutgoingEmail.objects.send(
|
||||
template_name="test_template",
|
||||
recipient="test@stardust.io", context={},
|
||||
sender="sklep-test@stardust.io"
|
||||
)
|
||||
self.assertEqual(mail.sent, True)
|
||||
# TODO outbox
|
||||
|
||||
def test_send_missing_template_failure(self):
|
||||
with self.assertRaises(MailTemplate.DoesNotExist):
|
||||
OutgoingEmail.objects.send(
|
||||
template_name="missing_template",
|
||||
recipient="", sender="", context={}\
|
||||
)
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
|
@ -1,7 +1,7 @@
|
|||
Django>=4.1,<4.2
|
||||
wagtail>=4.2,<4.3
|
||||
wagtailmenus>=3.1.5,<=3.1.7
|
||||
psycopg2>=2.9.5,<=2.9.6
|
||||
psycopg2-binary>=2.9.5,<=2.9.6
|
||||
dj-database-url<=2.0.0
|
||||
djangorestframework==3.14.0
|
||||
phonenumbers==8.13.13
|
||||
|
|
Ładowanie…
Reference in New Issue