membershipworks: Automatically send survey emails to event attendees
All checks were successful
Ruff / ruff (push) Successful in 29s
Test / test (push) Successful in 3m55s

This commit is contained in:
Adam Goldsmith 2024-05-23 19:20:01 -04:00
parent b1a7467eda
commit dcf483d19e
6 changed files with 130 additions and 0 deletions

View File

@ -7,6 +7,7 @@ def post_migrate_callback(sender, **kwargs):
from cmsmanage.django_q2_helper import ensure_scheduled from cmsmanage.django_q2_helper import ensure_scheduled
from .tasks.event_survey_emails import send_survey_emails
from .tasks.scrape import scrape_events, scrape_membershipworks from .tasks.scrape import scrape_events, scrape_membershipworks
from .tasks.ucsAccounts import sync_accounts from .tasks.ucsAccounts import sync_accounts
@ -29,6 +30,12 @@ def post_migrate_callback(sender, **kwargs):
minutes=15, minutes=15,
) )
ensure_scheduled(
sync_accounts.q_task_group,
send_survey_emails,
schedule_type=Schedule.HOURLY,
)
class MembershipworksConfig(AppConfig): class MembershipworksConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField" default_auto_field = "django.db.models.BigAutoField"

View File

@ -0,0 +1,17 @@
import logging
from django.core.management.base import BaseCommand
from membershipworks.tasks.event_survey_emails import logger, send_survey_emails
class Command(BaseCommand):
def handle(self, *args, verbosity: int, **options):
verbosity_levels = {
0: logging.ERROR,
1: logging.WARNING,
2: logging.INFO,
3: logging.DEBUG,
}
logger.setLevel(verbosity_levels.get(verbosity, logging.WARNING))
send_survey_emails()

View File

@ -0,0 +1,22 @@
# Generated by Django 5.0.6 on 2024-05-20 22:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("membershipworks", "0018_eventext_details_timestamp"),
]
operations = [
migrations.AddField(
model_name="eventext",
name="should_survey",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="eventext",
name="survey_email_sent",
field=models.BooleanField(default=False),
),
]

View File

@ -554,6 +554,9 @@ class EventExt(Event):
registrations = models.JSONField(null=True, blank=True) registrations = models.JSONField(null=True, blank=True)
should_survey = models.BooleanField(default=False)
survey_email_sent = models.BooleanField(default=False)
def get_absolute_url(self) -> str: def get_absolute_url(self) -> str:
return reverse("membershipworks:event-detail", kwargs={"eid": self.eid}) return reverse("membershipworks:event-detail", kwargs={"eid": self.eid})

View File

@ -0,0 +1,62 @@
import logging
from collections.abc import Iterable
from urllib.parse import quote, urlencode
from django.conf import settings
from django.core import mail
from django.db.models.functions import Now
from cmsmanage.email import TemplatedMultipartEmail
from membershipworks.models import EventExt
logger = logging.getLogger(__name__)
class EventSurveyEmail(TemplatedMultipartEmail):
# TODO: better wording
subject = (
"[Claremont MakerSpace] Please fill out a survey for your recent CMS class!"
)
from_email = "CMS Classes <classes@claremontmakerspace.org>"
template = "membershipworks/email/event_survey.dj.html"
@staticmethod
def survey_url(event: EventExt, attendee_name: str, attendee_email: str) -> str:
return "https://claremontmakerspace.org/class-evaluation-form?" + urlencode(
{
"event_id": event.eid,
"instructor_name": str(event.instructor) if event.instructor else "",
"event_name": event.title,
"event_date": event.start.strftime("%Y-%m-%d %H:%M:%S"),
"participant_name": attendee_name,
"participant_email": attendee_email,
},
quote_via=quote,
)
@classmethod
def render_for_event(cls, event: EventExt) -> Iterable[mail.EmailMessage]:
for name, email in event.attendees.values_list("name", "email"):
sanitized_email = mail.message.sanitize_address(
(name, email), settings.DEFAULT_CHARSET
)
survey_url = cls.survey_url(event, name, email)
yield cls.render(
{"event": event, "attendee_name": name, "survey_url": survey_url},
to=[sanitized_email],
)
def send_survey_emails():
with mail.get_connection() as conn:
for event in EventExt.objects.filter(
occurred=True, should_survey=True, survey_email_sent=False, end__lt=Now()
):
logger.info("Sending survey messages for event: %s", event)
# mark as sent even if we don't finish, to prevent sending duplicates
event.survey_email_sent = True
event.save()
conn.send_messages(list(EventSurveyEmail.render_for_event(event)))

View File

@ -0,0 +1,19 @@
{% load nh3_tags %}
<p>Dear {{ attendee_name }},</p>
<p>
Thank you for recently attending "{{ event.details.ttl|nh3 }}" at CMS on {{ event.start|date }}! We hope you enjoyed the experience and found it both informative and inspiring.
To help us continue to offer high-quality classes and improve our programs, we would greatly appreciate your feedback.
We kindly ask you to take a few minutes to complete a brief survey about your experience.
</p>
<p>
<a href="{{ survey_url }}">Click here to fill out the survey</a>
</p>
<p>
Your insights are invaluable to us and will directly contribute to enhancing our offerings and ensuring that we meet the needs and expectations of our community.
Thank you in advance for your time and feedback. If you have any additional comments or questions, please feel free to reach out to us at <a href="mailto:info@claremontmakerspace.org">info@claremontmakerspace.org</a>.
</p>
<p>
<div>Best regards,</div>
<div>The Claremont MakerSpace Team</div>
</p>