Skip to content

Commit

Permalink
Collect Email Activity from Mailersend
Browse files Browse the repository at this point in the history
  • Loading branch information
blopker committed Dec 15, 2024
1 parent 771eb25 commit 9496eae
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 128 deletions.
18 changes: 10 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
MAKEFLAGS += -j4
.PHONY: *

RUN_DJANGO = docker compose -f local.yml run --rm --remove-orphans django

run: up assets-watch

up:
Expand All @@ -18,13 +20,13 @@ test-js:
npm run test:ci

test-python:
docker compose -f local.yml run --rm django coverage run -m pytest -n auto
@${RUN_DJANGO} coverage run -m pytest -n auto

tasks:
docker compose -f local.yml run --rm django python manage.py totem_tasks
@${RUN_DJANGO} python manage.py totem_tasks

shell:
docker compose -f local.yml run --rm django bash
@${RUN_DJANGO} bash

dbshell:
docker compose -f local.yml exec postgres bash
Expand All @@ -33,7 +35,7 @@ sqlshell:
docker compose -f local.yml exec postgres psql -U debug -d totem

pyshell:
docker compose -f local.yml run --rm django ./manage.py shell_plus
@${RUN_DJANGO} ./manage.py shell_plus

deploy:
git push dokku
Expand All @@ -55,16 +57,16 @@ install_local: .venv
npm install

fixtures:
docker compose -f local.yml run --rm django python manage.py load_dev_data
@${RUN_DJANGO} python manage.py load_dev_data

migrations: ## Create DB migrations in the container
@docker compose -f local.yml run django python manage.py makemigrations
@${RUN_DJANGO} python manage.py makemigrations

migrate: ## Run DB migrations in the container
@docker compose -f local.yml run django python manage.py migrate
@${RUN_DJANGO} python manage.py migrate

generate_api_models:
@docker compose -f local.yml run --rm --remove-orphans django python manage.py export_openapi_schema --api totem.api.api.api > openapi.json
@${RUN_DJANGO} python manage.py export_openapi_schema --api totem.api.api.api > openapi.json
@npm run openapi-ts

updatedep:
Expand Down
3 changes: 3 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ def b64_json_env(key: str):
EMAIL_SUPPORT_ADDRESS = "[email protected]"
EMAIL_SHOW_ENV_BANNER = env.bool("EMAIL_SHOW_ENV_BANNER", default=False) # type: ignore
MAILERLITE_API_KEY = env("MAILERLITE_API_KEY", default="") # type: ignore
MAILERSEND_API_TOKEN = env("MAILERSEND_API_TOKEN", default="") # type: ignore
MAILERSEND_COLLECT_ACTIVITY = env.bool("MAILERSEND_COLLECT_ACTIVITY", default=False) # type: ignore
MAILERSEND_DOMAIN_ID = env("MAILERSEND_DOMAIN_ID", default="") # type: ignore
BREVO_API_KEY = env("BREVO_API_KEY", default="") # type: ignore
SEND_BREVO_EMAILS = env.bool("SEND_BREVO_EMAILS", default=False) # type: ignore

Expand Down
2 changes: 1 addition & 1 deletion config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

# INSTALLED_APPS += ["anymail"] # noqa: F405
# EMAIL_BACKEND = "anymail.backends.mailersend.EmailBackend"
# ANYMAIL = {"MAILERSEND_API_TOKEN": env("MAILERSEND_API_TOKEN"), "MAILERSEND_BATCH_SEND_MODE": "use-bulk-email"}
# ANYMAIL = {"MAILERSEND_API_TOKEN": MAILERSEND_API_TOKEN, "MAILERSEND_BATCH_SEND_MODE": "use-bulk-email"}

# WhiteNoise
# ------------------------------------------------------------------------------
Expand Down
6 changes: 3 additions & 3 deletions config/settings/production.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import socket

from .base import * # noqa
from .base import env
from .base import MAILERSEND_API_TOKEN, env

STATIC_HOST = STATIC_HOST or None # noqa: F405

Expand All @@ -28,7 +28,7 @@
# "LOCATION": env("REDIS_URL"),
# "OPTIONS": {
# "CLIENT_CLASS": "django_redis.client.DefaultClient",
# # Mimicing memcache behavior.
# # Mimicking memcache behavior.
# # https://github.com/jazzband/django-redis#memcached-exceptions-behavior
# "IGNORE_EXCEPTIONS": True,
# },
Expand Down Expand Up @@ -79,7 +79,7 @@
# # https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference
# # https://anymail.readthedocs.io/en/stable/esps/mailgun/
EMAIL_BACKEND = "anymail.backends.mailersend.EmailBackend"
ANYMAIL = {"MAILERSEND_API_TOKEN": env("MAILERSEND_API_TOKEN"), "MAILERSEND_BATCH_SEND_MODE": "use-bulk-email"}
ANYMAIL = {"MAILERSEND_API_TOKEN": MAILERSEND_API_TOKEN, "MAILERSEND_BATCH_SEND_MODE": "use-bulk-email"}


# LOGGING
Expand Down
94 changes: 47 additions & 47 deletions totem/circles/shotify.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
import asyncio
from io import BytesIO
# import asyncio
# from io import BytesIO

from PIL import Image
from playwright.async_api import async_playwright
# from PIL import Image
# from playwright.async_api import async_playwright


class ScreenshotRequestBody:
def __init__(self, url: str, selector: str):
self.url = url
self.selector = selector
# class ScreenshotRequestBody:
# def __init__(self, url: str, selector: str):
# self.url = url
# self.selector = selector


async def take_screenshot(request_body: ScreenshotRequestBody):
if not request_body.url or not request_body.selector:
raise Exception("Missing required fields: url or selector")
# async def take_screenshot(request_body: ScreenshotRequestBody):
# if not request_body.url or not request_body.selector:
# raise Exception("Missing required fields: url or selector")

browser = None
page = None
# browser = None
# page = None

try:
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page(device_scale_factor=2)
# try:
# async with async_playwright() as p:
# browser = await p.chromium.launch()
# page = await browser.new_page(device_scale_factor=2)

await page.set_viewport_size({"width": 1280, "height": 800})
await page.goto(request_body.url, wait_until="networkidle")
# await page.set_viewport_size({"width": 1280, "height": 800})
# await page.goto(request_body.url, wait_until="networkidle")

element = await page.query_selector(request_body.selector)
if not element:
raise Exception("Element not found")
# element = await page.query_selector(request_body.selector)
# if not element:
# raise Exception("Element not found")

screenshot_buffer = await element.screenshot(type="png")
image = Image.open(BytesIO(screenshot_buffer))
with BytesIO() as output_buffer:
image.save(output_buffer, format="WEBP", quality=80)
optimized_image_buffer = output_buffer.getvalue()
# screenshot_buffer = await element.screenshot(type="png")
# image = Image.open(BytesIO(screenshot_buffer))
# with BytesIO() as output_buffer:
# image.save(output_buffer, format="WEBP", quality=80)
# optimized_image_buffer = output_buffer.getvalue()

return optimized_image_buffer
# return optimized_image_buffer

finally:
if page:
await page.close()
if browser:
await browser.close()
# finally:
# if page:
# await page.close()
# if browser:
# await browser.close()


async def main():
try:
request_body = ScreenshotRequestBody(
url="https://www.totem.org/circles/event/zdx624ofr/social",
selector="[data-img]",
)
screenshot = await take_screenshot(request_body)
with open("screenshot.webp", "wb") as f:
f.write(screenshot)
print("Screenshot saved as screenshot.webp")
except Exception as e:
print("Error:", str(e))
# async def main():
# try:
# request_body = ScreenshotRequestBody(
# url="https://www.totem.org/circles/event/zdx624ofr/social",
# selector="[data-img]",
# )
# screenshot = await take_screenshot(request_body)
# with open("screenshot.webp", "wb") as f:
# f.write(screenshot)
# print("Screenshot saved as screenshot.webp")
# except Exception as e:
# print("Error:", str(e))


if __name__ == "__main__":
asyncio.run(main())
# if __name__ == "__main__":
# asyncio.run(main())
9 changes: 8 additions & 1 deletion totem/email/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin

from .models import EmailLog, SubscribedModel
from .models import EmailActivity, EmailLog, SubscribedModel


@admin.register(SubscribedModel)
Expand All @@ -19,3 +19,10 @@ class EmailLogAdmin(admin.ModelAdmin):
list_filter = ["template"]
readonly_fields = [field.name for field in EmailLog._meta.get_fields()]
actions = [clear_logs]


@admin.register(EmailActivity)
class EmailActivityAdmin(admin.ModelAdmin):
list_display = ("email", "event_type", "timestamp", "subject", "status")
search_fields = ("email", "subject", "event_type", "status")
list_filter = ("event_type", "status", "timestamp")
25 changes: 25 additions & 0 deletions totem/email/migrations/0007_emailactivity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.1.4 on 2024-12-15 17:00

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('email', '0006_remove_emaillog_body_remove_emaillog_subscribed_and_more'),
]

operations = [
migrations.CreateModel(
name='EmailActivity',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event', models.CharField(max_length=255)),
('timestamp', models.DateTimeField()),
('email', models.EmailField(max_length=254)),
('status', models.CharField(blank=True, max_length=255, null=True)),
('message_id', models.CharField(max_length=255, unique=True)),
('metadata', models.JSONField(blank=True, null=True)),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 5.1.4 on 2024-12-15 20:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('email', '0007_emailactivity'),
]

operations = [
migrations.RenameField(
model_name='emailactivity',
old_name='message_id',
new_name='activity_id',
),
migrations.RenameField(
model_name='emailactivity',
old_name='metadata',
new_name='data',
),
migrations.RemoveField(
model_name='emailactivity',
name='event',
),
migrations.AddField(
model_name='emailactivity',
name='event_type',
field=models.CharField(max_length=50, null=True),
),
migrations.AddField(
model_name='emailactivity',
name='subject',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AlterField(
model_name='emailactivity',
name='email',
field=models.EmailField(blank=True, max_length=254, null=True),
),
migrations.AlterField(
model_name='emailactivity',
name='id',
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name='emailactivity',
name='status',
field=models.CharField(blank=True, max_length=50, null=True),
),
]
25 changes: 25 additions & 0 deletions totem/email/migrations/0009_emailactivity_date_created_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.1.4 on 2024-12-15 21:04

import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('email', '0008_rename_message_id_emailactivity_activity_id_and_more'),
]

operations = [
migrations.AddField(
model_name='emailactivity',
name='date_created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='emailactivity',
name='date_modified',
field=models.DateTimeField(auto_now=True),
),
]
Loading

0 comments on commit 9496eae

Please sign in to comment.