Skip to content

Commit

Permalink
Add demo context functionality and improve email template parsing
Browse files Browse the repository at this point in the history
Introduce `demo_context` field for email template previews, with support for constructing and reparsing contexts dynamically. Enhance parsing logic for nested variables in templates and extend test coverage. Update Docker configurations for Celery, and streamline admin interfaces with new context actions.
  • Loading branch information
michaelpoi committed Dec 20, 2024
1 parent 0fc7547 commit 4968b61
Show file tree
Hide file tree
Showing 20 changed files with 309 additions and 78 deletions.
1 change: 1 addition & 0 deletions demoapp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
('email/in.html', _('Incoming')),
('email/uni.html', _('University')),
('email/contexed_uni.html', _('Contexed')),
('email/test_updated_parser.html', 'Test updated parser'),
('email/tracker.html', _('Tracker')),
],
}
Expand Down
2 changes: 2 additions & 0 deletions demoapp/templates/email/contexed_uni.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
<tr>
<td align="left" valign="top" style="text-align:justify;">
{% for new in news %}
{{ new.some.dur }}
{{ new.some.val }}
<table width="300" align="left" border="0" cellspacing="0" cellpadding="0" style="display:inline;width:300px;">
<tbody><tr>
<td><a href="http://www.uibk.ac.at/zid/intranet/newsletter/2024-10/vorwort.html" target="_blank" rel="noopener noreferrer" style="text-decoration:none;"><img src="{{ new.image }}" width="300" height="180" border="0" alt="Hello" style="max-width:100%;"></a></td>
Expand Down
24 changes: 24 additions & 0 deletions demoapp/templates/email/test_updated_parser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Some</title>
</head>
<body>
{{ controller.topic }}
{{ controller.stats.likes.bots }}
{{ controller.stats.stars }}
{{ controller.stats.likes.people }}


{% for new in updates %}
{{ controller.logic }}
{% for like in new.likes %}
{{ like.name }}
{{ new.something_else }}
{% endfor %}
{{ new.interactions }}
{{ new.extra_values.reposts }}
{% endfor %}
</body>
</html>
21 changes: 21 additions & 0 deletions demoapp/templates/test/nested_jsons.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Some</title>
</head>
<body>
{{ controller.topic }}
{{ controller.stats.stars }}
{{ controller.stats.likes }}

{% for new in updates %}
{{ controller.subtopic }}
{% for upd in new.changes %}
{{ controller.subsubtopic }}
{% endfor %}
{{ new.interactions }}
{{ new.extra_values.reposts }}
{% endfor %}
</body>
</html>
30 changes: 25 additions & 5 deletions demoapp/tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,36 @@ def test_extract_vars():
{'var1': '',
'var_true': '',
'var_false': '',
'list': [
{
'var_loop': ''
}
],
'var_loop': '',
'var_inner': '',
'var_inner_true': '',
'var_block': ''})


def test_parse_nested_objects():
data = {
'controller': {
'stats': {
'likes': '',
'stars': '',
},
'topic': '',
'subtopic': '',
'subsubtopic': ''
},
'updates': [
{
'extra_values': {
'reposts': '',
},
'interactions': '',
},
],
}

assert extract_variable_names('test/nested_jsons.html') == data


@pytest.mark.django_db
def test_ckeditor_vars(template):
placeholder1 = PlaceholderContent.objects.get(placeholder_name='test1', language='en')
Expand Down
4 changes: 4 additions & 0 deletions demoapp/tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,7 @@ def test_render_template(test_template):
'</html>').strip()

assert clean == html_string

@pytest.mark.django_db
def test_demo_context(template):
assert template.demo_context == {'test_var': ''}
13 changes: 13 additions & 0 deletions deploy/celery/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.11

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN pip install pip --upgrade
COPY . /app

RUN pip install -r /app/demoapp/tests/requirements/test_requirements.txt

WORKDIR /app

CMD ["python", "-m", "celery", "-A", "demoapp", "worker", "-l", "info", "--concurrency=5"]
13 changes: 13 additions & 0 deletions deploy/celery_beat/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.11

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN pip install pip --upgrade
COPY . /app

RUN pip install -r /app/demoapp/tests/requirements/test_requirements.txt

WORKDIR /app

CMD ["python", "-m", "celery", "-A", "demoapp", "beat", "-l", "info"]
1 change: 1 addition & 0 deletions deploy/sendmail/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ WORKDIR /app/demoapp
RUN pip install -r /app/demoapp/tests/requirements/test_requirements.txt
RUN pip install uWSGI

RUN python manage.py makemigrations --no-input
RUN python manage.py migrate --no-input
RUN python manage.py collectstatic --no-input

Expand Down
29 changes: 24 additions & 5 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:

environment:
- USE_POSTGRES=True
- DB_NAME=sendmail
- DB_NAME=sendmail_db
- DB_USER=sendmail
- DB_PASSWORD=sendmail
- DB_HOST=db
Expand All @@ -34,7 +34,7 @@ services:
volumes:
- postgres_data:/var/lib/posgresql/data
environment:
- POSTGRES_DB=sendmail
- POSTGRES_DB=sendmail_db
- POSTGRES_USER=sendmail
- POSTGRES_PASSWORD=sendmail
ports:
Expand All @@ -52,8 +52,7 @@ services:
container_name: celery
build:
context: .
dockerfile: ./deploy/sendmail/Dockerfile
command: python -m celery -A demoapp worker -l info --concurrency=5
dockerfile: ./deploy/celery/Dockerfile
volumes:
- static:/app/staticfiles
- media:/app/workdir/media
Expand All @@ -62,7 +61,27 @@ services:
- sendmail
environment:
- USE_POSTGRES=True
- DB_NAME=sendmail
- DB_NAME=sendmail_db
- DB_USER=sendmail
- DB_PASSWORD=sendmail
- DB_HOST=db
- DB_PORT=5432
- CELERY_BROKER_URL=redis://redis:6379/0 # Use Redis as the broker
- EMAIL_HOST=mailpit

celery_beat:
container_name: celery_beat
build:
context: .
dockerfile: ./deploy/celery/Dockerfile
command: python -m celery -A demoapp beat -l info
depends_on:
- redis
- sendmail
- celery
environment:
- USE_POSTGRES=True
- DB_NAME=sendmail_db
- DB_USER=sendmail
- DB_PASSWORD=sendmail
- DB_HOST=db
Expand Down
27 changes: 22 additions & 5 deletions sendmail/admin/emailmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db import models
from django.db.models import Case, IntegerField, Value, When
from django.forms import BaseInlineFormSet, TextInput
from django.db.models.fields.json import JSONField
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.text import Truncator
Expand All @@ -17,6 +18,11 @@
from sendmail.models.emailmodel import STATUS
from sendmail.settings import get_default_language, get_email_templates, get_languages_list

try:
from jsoneditor.forms import JSONEditor
except ImportError:
JSONEditor = None


class SubjectField(TextInput):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -147,9 +153,10 @@ class EmailMergeAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name', 'description', 'template_file']}),
(_("Extra Recipients"), {'fields': ['extra_recipients'], 'classes': ['collapse']}),
(_("Demo Context"), {'fields': ['demo_context'], 'classes': ['collapse']}),
]
inlines = [EmailMergeContentInline, PlaceholderContentInline]
formfield_overrides = {models.CharField: {'widget': SubjectField}}
formfield_overrides = {models.CharField: {'widget': SubjectField}, JSONField: {'widget': JSONEditor}}
filter_horizontal = ['extra_recipients']

class Media:
Expand All @@ -166,7 +173,7 @@ def send_email_view(self, request, obj):
return

try:
email = send(recipients=admin_email, emailmerge=obj, priority='now', language=language)
email = send(recipients=admin_email, emailmerge=obj, priority='now', language=language, context=obj.demo_context)
if email.status == STATUS.sent:
messages.success(request, "Email sent successfully to {admin_email}".format(admin_email=admin_email))
else:
Expand All @@ -188,19 +195,29 @@ def change_view(self, request, object_id, form_url="", extra_context=None):
language_choices = []

extra_context['show_send'] = True
extra_context['show_reparse'] = True
extra_context['language_options'] = language_choices
extra_context['email'] = email
return super().change_view(request, str(object_id), form_url=form_url, extra_context=extra_context)

def response_change(self, request, obj):
if "_send_email" in request.POST:
self.send_email_view(request, obj)
return redirect(

redirect_response = redirect(
reverse(
'admin:%s_%s_change' % (self.model._meta.app_label, self.model._meta.model_name),
args=[obj.pk]
))

if "_send_email" in request.POST:
self.send_email_view(request, obj)
return redirect_response

if "_reparse" in request.POST:
obj.reparse_context()
return redirect_response



return super().response_change(request, obj)

def description_shortened(self, instance):
Expand Down
22 changes: 17 additions & 5 deletions sendmail/admin/newsletter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from django.contrib import admin
from django.db.models.fields.json import JSONField
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from sendmail.admin.admin_utils import get_language_name
from sendmail.models.emailmodel import STATUS, EmailModel
Expand Down Expand Up @@ -50,15 +53,15 @@ def get_fieldsets(self, request, obj = None):
(None, {
'fields': ('name', 'emailmerge', 'to_recipients', 'email_from', 'language'),
}),
('Schedule', {
(_('Schedule'), {
'fields': ('scheduled_time', 'expires_at', 'priority'),
'classes': ('collapse',)
}),
('Context', {
(_('Context'), {
'fields': ('context',),
'classes': ('collapse',),
}),
('Headers', {
(_('Headers'), {
'fields': ('headers',),
'classes': ('collapse',),
}),
Expand All @@ -70,7 +73,7 @@ def get_fieldsets(self, request, obj = None):
if get_tracking_enabled():
fields.extend(['opened', 'clicked'])

fieldsets.append(('Result', {
fieldsets.append((_('Result'), {
'fields': fields,
'classes': ('collapse',)
}))
Expand Down Expand Up @@ -148,7 +151,7 @@ def has_change_permission(self, request, obj=None):
def queued_emails(self, obj):
return EmailModel.objects.filter(newsletter=obj, status=STATUS.queued).count()

queued_emails.short_description = 'Queued Emails'
queued_emails.short_description = _('Queued Emails')

def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
Expand All @@ -159,10 +162,19 @@ def change_view(self, request, object_id, form_url="", extra_context=None):
can_send = True

extra_context['show_newsletter_send'] = can_send
extra_context['show_reparse'] = can_send
return super().change_view(request, str(object_id), form_url=form_url, extra_context=extra_context)

def response_change(self, request, obj):
if "_send_many" in request.POST:
obj.create()

if "_reparse" in request.POST:
obj.reparse_context()
return redirect(
reverse(
'admin:%s_%s_change' % (self.model._meta.app_label, self.model._meta.model_name),
args=[obj.pk]
))

return super().response_change(request, obj)
18 changes: 18 additions & 0 deletions sendmail/migrations/0032_emailmergemodel_demo_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2024-12-20 08:01

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('sendmail', '0031_remove_newsletter_attachments_and_more'),
]

operations = [
migrations.AddField(
model_name='emailmergemodel',
name='demo_context',
field=models.JSONField(blank=True, help_text='Example context for previewing the email template.', null=True, verbose_name='Demo context'),
),
]
Loading

0 comments on commit 4968b61

Please sign in to comment.