From 6e44ffeaf697fcae7dbbb33ff57d2024b039aca0 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Thu, 28 Nov 2024 22:21:41 -0600 Subject: [PATCH 01/17] Revert "Merge pull request #497 from tamuhack-org/sandeep-dev" This reverts commit 873fefe015530bba88700fbe575266bc2d881b71, reversing changes made to c4bea7ed39b866f1fddc24768b597c3685a64dc8. --- dump.rdb | Bin 88 -> 0 bytes hiss/Procfile | 2 - hiss/application/admin.py | 4 +- hiss/application/emails.py | 110 ++++++++++++++----------- hiss/application/fixtures/schools.json | 4 +- hiss/application/meal_groups.py | 59 ------------- hiss/application/models.py | 87 +------------------ hiss/application/views.py | 9 +- hiss/customauth/views.py | 6 +- hiss/hiss/__init__.py | 4 - hiss/hiss/celery.py | 22 ----- hiss/hiss/settings/dev.py | 10 --- hiss/requirements.txt | 2 - hiss/shared/admin_functions.py | 51 +++++++++--- hiss/user/models.py | 39 +++++---- 15 files changed, 135 insertions(+), 274 deletions(-) delete mode 100644 dump.rdb delete mode 100644 hiss/Procfile delete mode 100644 hiss/application/meal_groups.py delete mode 100644 hiss/hiss/celery.py diff --git a/dump.rdb b/dump.rdb deleted file mode 100644 index 0958dab0b78a552c5daedea626ad86c5c7011362..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88 zcmWG?b@2=~FfcUy#aWb^l3A=H&uT<_tS!vj4nl1OUE5BkBME diff --git a/hiss/Procfile b/hiss/Procfile deleted file mode 100644 index d48c4bff..00000000 --- a/hiss/Procfile +++ /dev/null @@ -1,2 +0,0 @@ -web: gunicorn hiss.wsgi --log-file - -worker: celery -A hiss worker --loglevel=info \ No newline at end of file diff --git a/hiss/application/admin.py b/hiss/application/admin.py index 76506b6b..45ef2bd8 100644 --- a/hiss/application/admin.py +++ b/hiss/application/admin.py @@ -104,7 +104,7 @@ def approve(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: print(f"approval email built for {approval_email[-1:]}") email_tuples.append(approval_email) application.save() - send_mass_html_mail.delay(email_tuples) + send_mass_html_mail(email_tuples) def reject(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: @@ -117,7 +117,7 @@ def reject(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: application.status = STATUS_REJECTED email_tuples.append(build_rejection_email(application)) application.save() - send_mass_html_mail.delay(email_tuples) + send_mass_html_mail(email_tuples) def resend_confirmation(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 052c8dcb..0e09d788 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -1,14 +1,44 @@ -from celery import shared_task import json from io import BytesIO + import pyqrcode +from django.conf import settings from django.core import mail from django.template.loader import render_to_string from django.utils import html + from application.models import Application from application.apple_wallet import get_apple_wallet_pass_url -from django.conf import settings -from django.core.mail import EmailMultiAlternatives + +import threading +from django.core.mail import EmailMessage + +#create separate threading class for confirmation email since it has a QR code + +class EmailQRThread(threading.Thread): + def __init__(self, subject, msg, html_msg, recipient_email, qr_stream): + self.subject = subject + self.msg = msg + self.html_msg = html_msg + self.recipient_email = recipient_email + self.qr_stream = qr_stream + threading.Thread.__init__(self) + + def run(self): + qr_code = pyqrcode.create(self.qr_content) + qr_stream = BytesIO() + qr_code.png(qr_stream, scale=5) + + email = mail.EmailMultiAlternatives( + self.subject, self.msg, from_email=None, to=[self.recipient_email] + ) + email.attach_alternative(self.html_msg, "text/html") + email.attach("code.png", self.qr_stream.getvalue(), "text/png") + + # if above code is defined directly in function, it will run synchronously + # therefore need to directly define in threading class to run asynchronously + + email.send() def send_creation_email(app: Application) -> None: """ @@ -29,56 +59,38 @@ def send_creation_email(app: Application) -> None: # send_html_email is threaded from the User class # see user/models.py - app.user.send_html_email.delay(template_name, context, subject) + app.user.send_html_email(template_name, context, subject) -@shared_task -def send_confirmation_email(app_id: int) -> None: + +def send_confirmation_email(app: Application) -> None: """ Sends a confirmation email to a user, which contains their QR code as well as additional event information. - :param app_id: The ID of the user's application - :type app_id: int + :param app: The user's application + :type app: Application :return: None """ - try: - app = Application.objects.get(id=app_id) - subject = f"TAMUhack: Important Day-Of Information" - email_template = "application/emails/confirmed.html" - - if app.status == "E": - subject = f"TAMUhack Waitlist: Important Day-of Information!" - email_template = "application/emails/confirmed-waitlist.html" - - context = { + subject = f"TAMUhack: Important Day-Of Information" + email_template = "application/emails/confirmed.html" + context = { + "first_name": app.first_name, + "event_name": settings.EVENT_NAME, + "organizer_name": settings.ORGANIZER_NAME, + "event_year": settings.EVENT_YEAR, + "organizer_email": settings.ORGANIZER_EMAIL, + "apple_wallet_url": get_apple_wallet_pass_url(app.user.email), + "event_date_text": settings.EVENT_DATE_TEXT, + } + html_msg = render_to_string(email_template, context) + plain_msg = html.strip_tags(html_msg) + + qr_content = json.dumps( + { "first_name": app.first_name, - "event_name": settings.EVENT_NAME, - "organizer_name": settings.ORGANIZER_NAME, - "event_year": settings.EVENT_YEAR, - "organizer_email": settings.ORGANIZER_EMAIL, - "apple_wallet_url": get_apple_wallet_pass_url(app.user.email), - "event_date_text": settings.EVENT_DATE_TEXT, + "last_name": app.last_name, + "email": app.user.email, + "university": app.school.name, } - - html_msg = render_to_string(email_template, context) - plain_msg = html.strip_tags(html_msg) - email = mail.EmailMultiAlternatives( - subject, plain_msg, from_email=None, to=[app.user.email] - ) - - email.attach_alternative(html_msg, "text/html") - qr_content = json.dumps( - { - "first_name": app.first_name, - "last_name": app.last_name, - "email": app.user.email, - "university": app.school.name, - } - ) - - qr_code = pyqrcode.create(qr_content) - qr_stream = BytesIO() - qr_code.png(qr_stream, scale=5) - email.attach("code.png", qr_stream.getvalue(), "image/png") - print(f"sending confirmation email to {app.user.email}") - email.send() - except Exception as e: - print(f"Error sending confirmation email: {e}") \ No newline at end of file + ) + + email_thread = EmailQRThread(subject, plain_msg, html_msg, app.user.email, qr_content) + email_thread.start() diff --git a/hiss/application/fixtures/schools.json b/hiss/application/fixtures/schools.json index 38c267dd..8a5fad80 100644 --- a/hiss/application/fixtures/schools.json +++ b/hiss/application/fixtures/schools.json @@ -10984,7 +10984,7 @@ }, { "model": "application.school", - "pk": 2074, + "pk": 2075, "fields": { "name": "Texas A&M University - San Antonio" } @@ -14519,7 +14519,7 @@ }, { "model": "application.school", - "pk": 2075, + "pk": 2074, "fields": { "name": "Other" } diff --git a/hiss/application/meal_groups.py b/hiss/application/meal_groups.py deleted file mode 100644 index ff946ecd..00000000 --- a/hiss/application/meal_groups.py +++ /dev/null @@ -1,59 +0,0 @@ -from django.db import transaction -from models import Application - -NUM_GROUPS = 4 -RESTRICTED_FRONTLOAD_FACTOR = 1.3 - -def assign_food_groups(): - veg_apps = [] - nobeef_apps = [] - nopork_apps = [] - allergy_apps = [] - othernonveg_apps = [] - - applicants = Application.objects.filter(status__in=['A', 'E', 'C']) - - for app in applicants: - if "Vegetarian" in app.dietary_restrictions or "Vegan" in app.dietary_restrictions: - veg_apps.append(app) - elif "No-Beef" in app.dietary_restrictions: - nobeef_apps.append(app) - elif "No-Pork" in app.dietary_restrictions: - nopork_apps.append(app) - elif "Food-Allergy" in app.dietary_restrictions: - allergy_apps.append(app) - else: - othernonveg_apps.append(app) - - restricted_apps = veg_apps + nobeef_apps + nopork_apps + allergy_apps - num_apps = len(restricted_apps) + len(othernonveg_apps) - - group_size = num_apps // NUM_GROUPS - restricted_percent = len(restricted_apps) / num_apps - restricted_target = restricted_percent * RESTRICTED_FRONTLOAD_FACTOR - restricted_per_group = restricted_target * group_size - - groups = [[] for _ in range(NUM_GROUPS)] - group_restricted_count = [0] * NUM_GROUPS - - # Assign restricted applicants - for i in range(NUM_GROUPS): - groups[i] = restricted_apps[:int(restricted_per_group)] - restricted_apps = restricted_apps[int(restricted_per_group):] - group_restricted_count[i] = len(groups[i]) - - # Assign unrestricted applicants - for i in range(NUM_GROUPS): - groups[i] += othernonveg_apps[:group_size - group_restricted_count[i]] - othernonveg_apps = othernonveg_apps[group_size - group_restricted_count[i]:] - groups[-1] += othernonveg_apps - - # Update database with meal groups - with transaction.atomic(): - for i, group in enumerate(groups): - group_letter = chr(65 + i) - for app in group: - app.meal_group = group_letter - app.save() - - return {f"Group {chr(65 + i)}": len(group) for i, group in enumerate(groups)} diff --git a/hiss/application/models.py b/hiss/application/models.py index d6847a6e..df165475 100644 --- a/hiss/application/models.py +++ b/hiss/application/models.py @@ -401,92 +401,7 @@ class Application(models.Model): user = models.ForeignKey("user.User", on_delete=models.CASCADE, null=False) status = models.CharField( choices=STATUS_OPTIONS, max_length=1, default=STATUS_PENDING - ) - - # def get_next_meal_group(self): - # """ - # Determines the next meal group considering frontloading for restricted groups - # using the RESTRICTED_FRONTLOAD_FACTOR. - # """ - # RESTRICTED_FRONTLOAD_FACTOR = 1.3 - - # meal_groups = ['A', 'B', 'C', 'D'] - # group_distribution = {group: 0 for group in meal_groups} - # restricted_distribution = {group: 0 for group in meal_groups} - # total_confirmed = Application.objects.filter(status='C').exclude(meal_group__isnull=True) - - # for group in meal_groups: - # group_distribution[group] = total_confirmed.filter(meal_group=group).count() - # restricted_distribution[group] = total_confirmed.filter( - # meal_group=group, - # dietary_restrictions__icontains="Vegetarian" - # ).count() + total_confirmed.filter( - # meal_group=group, - # dietary_restrictions__icontains="No-Beef" - # ).count() + total_confirmed.filter( - # meal_group=group, - # dietary_restrictions__icontains="No-Pork" - # ).count() + total_confirmed.filter( - # meal_group=group, - # dietary_restrictions__icontains="Food-Allergy" - # ).count() - - # total_apps = sum(group_distribution.values()) - # total_restricted = sum(restricted_distribution.values()) - # if total_apps == 0: - # return meal_groups[0] - - # base_restricted_percent = total_restricted / total_apps if total_apps else 0 - # target_restricted_percent = { - # group: base_restricted_percent * (RESTRICTED_FRONTLOAD_FACTOR if i < len(meal_groups) // 2 else 1.0) - # for i, group in enumerate(meal_groups) - # } - # target_restricted_count = { - # group: target_restricted_percent[group] * group_distribution[group] if group_distribution[group] > 0 else 0 - # for group in meal_groups - # } - - # restricted_gap = { - # group: target_restricted_count[group] - restricted_distribution[group] - # for group in meal_groups - # } - # prioritized_group = max(restricted_gap, key=restricted_gap.get) - - # last_assigned_group = ( - # total_confirmed.order_by('-datetime_submitted') - # .values_list('meal_group', flat=True) - # .first() - # ) - # if last_assigned_group: - # next_index = (meal_groups.index(last_assigned_group) + 1) % len(meal_groups) - # else: - # next_index = 0 - - # # use frontloaded group if it aligns with round-robin or is significantly better - # if prioritized_group == meal_groups[next_index]: - # return meal_groups[next_index] - # elif restricted_gap[prioritized_group] > restricted_gap[meal_groups[next_index]]: - # return prioritized_group - # else: - # return meal_groups[next_index] - - # def assign_meal_group(self): - # """ - # Assigns a meal group based on the current status and dietary restrictions. - # """ - # if self.status == 'C': # Confirmed - # self.meal_group = self.get_next_meal_group() - # elif self.status == 'E': # Waitlisted - # self.meal_group = 'E' - # else: - # self.meal_group = None - - # def save(self, *args, **kwargs): - # """ - # Overrides save to ensure meal group assignment logic is applied. - # """ - # self.assign_meal_group() - # super().save(*args, **kwargs) + ) # ABOUT YOU first_name = models.CharField( diff --git a/hiss/application/views.py b/hiss/application/views.py index 88a116c7..128959de 100644 --- a/hiss/application/views.py +++ b/hiss/application/views.py @@ -6,7 +6,7 @@ from django.shortcuts import redirect from django.urls import reverse_lazy from django.views import generic -from django.db import transaction + from application.emails import send_confirmation_email, send_creation_email from application.forms import ApplicationModelForm from application.models import ( @@ -103,10 +103,9 @@ def post(self, request: HttpRequest, *args, **kwargs): raise PermissionDenied( "You can't confirm your application if it hasn't been approved." ) - with transaction.atomic(): - app.status = STATUS_CONFIRMED - app.save() - send_confirmation_email(app) + app.status = STATUS_CONFIRMED + app.save() + send_confirmation_email(app) return redirect(reverse_lazy("status")) diff --git a/hiss/customauth/views.py b/hiss/customauth/views.py index 34e3a6ec..12b67b69 100644 --- a/hiss/customauth/views.py +++ b/hiss/customauth/views.py @@ -7,7 +7,7 @@ from django.http import HttpResponse from django.shortcuts import redirect, render, get_object_or_404 from django.urls import reverse_lazy -from django.utils.encoding import force_bytes, force_str +from django.utils.encoding import force_bytes, force_text from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.views import generic @@ -26,7 +26,7 @@ def send_confirmation_email(curr_domain: RequestSite, user: User) -> None: "event_name": settings.EVENT_NAME, "organizer_name": settings.ORGANIZER_NAME, } - user.send_html_email.delay(template_name, context, subject) + user.send_html_email(template_name, context, subject) # Create your views here. @@ -58,7 +58,7 @@ class ActivateView(views.View): def get(self, request, *_args, **kwargs): user = None try: - uid = force_str(urlsafe_base64_decode(kwargs["uidb64"])) + uid = force_text(urlsafe_base64_decode(kwargs["uidb64"])) user = get_user_model().objects.get(id=int(uid)) except ( TypeError, diff --git a/hiss/hiss/__init__.py b/hiss/hiss/__init__.py index e0b971ee..e69de29b 100644 --- a/hiss/hiss/__init__.py +++ b/hiss/hiss/__init__.py @@ -1,4 +0,0 @@ -from __future__ import absolute_import, unicode_literals -from .celery import app as celery_app - -__all__ = ('celery_app',) \ No newline at end of file diff --git a/hiss/hiss/celery.py b/hiss/hiss/celery.py deleted file mode 100644 index 56a45d05..00000000 --- a/hiss/hiss/celery.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import absolute_import, unicode_literals -import os -from celery import Celery - -# Set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hiss.settings.dev') - -app = Celery('hiss') - -# Using a string here so the worker doesn't have to serialize the object to child processes. -app.config_from_object('django.conf:settings', namespace='CELERY') - -# Load task modules from all registered Django app configs. -app.autodiscover_tasks() - -# Celery configuration for Redis as the broker and result backend -app.conf.broker_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') # Redis as broker -app.conf.result_backend = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') # Redis as result backend - -@app.task(bind=True) -def debug_task(self): - print(f'Request: {self.request!r}') diff --git a/hiss/hiss/settings/dev.py b/hiss/hiss/settings/dev.py index 829bfe3c..2d3fd32a 100644 --- a/hiss/hiss/settings/dev.py +++ b/hiss/hiss/settings/dev.py @@ -4,8 +4,6 @@ # noinspection PyUnresolvedReferences from .customization import * -import os - SECRET_KEY = "development" DEBUG = True # Database @@ -27,11 +25,3 @@ MEDIA_ROOT = "resumes" AWS_S3_KEY_PREFIX = "dev-resumes" - -CELERY_ACCEPT_CONTENT = ['json'] -CELERY_TASK_SERIALIZER = 'json' -CELERY_RESULT_SERIALIZER = 'json' -CELERY_TIMEZONE = 'UTC' -CELERY_ENABLE_UTC = True -CELERY_BROKER_URL = os.environ.get('REDIS_URL') -CELERY_RESULT_BACKEND = os.environ.get('REDIS_URL') \ No newline at end of file diff --git a/hiss/requirements.txt b/hiss/requirements.txt index 650e2c59..ed3d495d 100644 --- a/hiss/requirements.txt +++ b/hiss/requirements.txt @@ -54,5 +54,3 @@ typed-ast==1.4.0 urllib3==1.25.6 whitenoise==5.2.0 wrapt==1.11.2 -celery==4.4.7 -redis \ No newline at end of file diff --git a/hiss/shared/admin_functions.py b/hiss/shared/admin_functions.py index 0185a2ad..85f1de6e 100644 --- a/hiss/shared/admin_functions.py +++ b/hiss/shared/admin_functions.py @@ -1,19 +1,46 @@ -from celery import shared_task from django.core.mail import get_connection, EmailMultiAlternatives +import threading -@shared_task -def send_mass_html_mail(datatuple, fail_silently=False, user=None, password=None): +#create separate Threading class for mass emails +class MassEmailThread(threading.Thread): + def __init__(self, subject, text_content, html_content, from_email, recipient_list, connection): + threading.Thread.__init__(self) + self.subject = subject + self.text_content = text_content + self.html_content = html_content + self.from_email = from_email + self.recipient_list = recipient_list + self.connection = connection + self.result = 0 + + def run(self): + email = EmailMultiAlternatives(self.subject, self.text_content, self.from_email, self.recipient_list) + email.attach_alternative(self.html_content, "text/html") + try: + self.result = email.send(fail_silently=False, connection=self.connection) + except Exception as e: + print("Error sending email: ", e) + self.result = 0 + +def send_mass_html_mail(datatuple, fail_silently=False, user=None, password=None, connection=None): """ - Celery task to send multiple HTML emails given a datatuple of - (subject, text_content, html_content, from_email, recipient_list). + Sends each message in datatuple (subject, text_content, html_content, from_email, recipient_list). + Returns the number of emails sent. """ - connection = get_connection( + connection = connection or get_connection( username=user, password=password, fail_silently=fail_silently ) - messages = [] + + threads = [] + for subject, text, html, from_email, recipient in datatuple: - message = EmailMultiAlternatives(subject, text, from_email, recipient) - message.attach_alternative(html, "text/html") - messages.append(message) - - return connection.send_messages(messages) + email_thread = MassEmailThread(subject, text, html, from_email, recipient, connection) + email_thread.start() + threads.append(email_thread) + + for thread in threads: + thread.join() + + total_sent = sum(thread.result for thread in threads if thread.result) + + return total_sent \ No newline at end of file diff --git a/hiss/user/models.py b/hiss/user/models.py index a1d0aefd..c0a040fd 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -7,11 +7,7 @@ from django.template.loader import render_to_string from django.utils import html from rest_framework.authtoken.models import Token - -from celery import shared_task -from django.core.mail import send_mail -from django.template.loader import render_to_string -from django.utils.html import strip_tags +import threading class EmailUserManager(auth_models.UserManager): """ @@ -40,6 +36,23 @@ def create_superuser( extra_fields.setdefault("is_active", True) return self._create_user(email, password, **extra_fields) +class EmailThread(threading.Thread): + def __init__(self, subject, plain_message, recipient_email, html_message): + self.subject = subject + self.plain_message = plain_message + self.recipient_email = recipient_email + self.html_message = html_message + threading.Thread.__init__(self) + + def run(self): + mail.send_mail( + subject=self.subject, + message=self.plain_message, + from_email= settings.ORGANIZER_EMAIL, + recipient_list=[self.recipient_email], + html_message=self.html_message, + ) + class User(auth_models.AbstractUser): """ A representation of a user within the registration system. Users are uniquely identified by their email, @@ -74,18 +87,12 @@ class User(auth_models.AbstractUser): USERNAME_FIELD = "email" REQUIRED_FIELDS = [] - @shared_task - def send_html_email(template_name, context, subject, recipient_email): - """Celery task to send an HTML email.""" + def send_html_email(self, template_name, context, subject): + """Send an HTML email to the user.""" html_msg = render_to_string(template_name, context) - plain_msg = strip_tags(html_msg) - send_mail( - subject=subject, - message=plain_msg, - from_email=settings.ORGANIZER_EMAIL, - recipient_list=[recipient_email], - html_message=html_msg, - ) + plain_msg = html.strip_tags(html_msg) + email_thread = EmailThread(subject, plain_msg, self.email, html_msg) + email_thread.start() @receiver(post_save, sender=settings.AUTH_USER_MODEL) From ac9a34ee56508515f2912a17391d148769489898 Mon Sep 17 00:00:00 2001 From: Sandeep Kandrigi <108851203+skandrigi@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:58:16 -0600 Subject: [PATCH 02/17] Revert "Updated task queue code" From b015bd3bf401ab4254040dec9a732c1de4e702e7 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 01:34:13 -0600 Subject: [PATCH 03/17] add task queue for emails to send asynchronously --- dump.rdb | Bin 0 -> 88 bytes hiss/Procfile | 2 + hiss/application/admin.py | 4 +- hiss/application/emails.py | 110 +++++++++++++++------------------ hiss/hiss/__init__.py | 4 ++ hiss/hiss/celery.py | 23 +++++++ hiss/hiss/settings/dev.py | 10 +++ hiss/requirements.txt | 2 + hiss/shared/admin_functions.py | 51 ++++----------- hiss/user/models.py | 39 +++++------- 10 files changed, 120 insertions(+), 125 deletions(-) create mode 100644 dump.rdb create mode 100644 hiss/Procfile create mode 100644 hiss/hiss/celery.py diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000000000000000000000000000000000000..0958dab0b78a552c5daedea626ad86c5c7011362 GIT binary patch literal 88 zcmWG?b@2=~FfcUy#aWb^l3A=H&uT<_tS!vj4nl1OUE5BkBME literal 0 HcmV?d00001 diff --git a/hiss/Procfile b/hiss/Procfile new file mode 100644 index 00000000..d48c4bff --- /dev/null +++ b/hiss/Procfile @@ -0,0 +1,2 @@ +web: gunicorn hiss.wsgi --log-file - +worker: celery -A hiss worker --loglevel=info \ No newline at end of file diff --git a/hiss/application/admin.py b/hiss/application/admin.py index 45ef2bd8..76506b6b 100644 --- a/hiss/application/admin.py +++ b/hiss/application/admin.py @@ -104,7 +104,7 @@ def approve(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: print(f"approval email built for {approval_email[-1:]}") email_tuples.append(approval_email) application.save() - send_mass_html_mail(email_tuples) + send_mass_html_mail.delay(email_tuples) def reject(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: @@ -117,7 +117,7 @@ def reject(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: application.status = STATUS_REJECTED email_tuples.append(build_rejection_email(application)) application.save() - send_mass_html_mail(email_tuples) + send_mass_html_mail.delay(email_tuples) def resend_confirmation(_modeladmin, _request: HttpRequest, queryset: QuerySet) -> None: diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 0e09d788..052c8dcb 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -1,44 +1,14 @@ +from celery import shared_task import json from io import BytesIO - import pyqrcode -from django.conf import settings from django.core import mail from django.template.loader import render_to_string from django.utils import html - from application.models import Application from application.apple_wallet import get_apple_wallet_pass_url - -import threading -from django.core.mail import EmailMessage - -#create separate threading class for confirmation email since it has a QR code - -class EmailQRThread(threading.Thread): - def __init__(self, subject, msg, html_msg, recipient_email, qr_stream): - self.subject = subject - self.msg = msg - self.html_msg = html_msg - self.recipient_email = recipient_email - self.qr_stream = qr_stream - threading.Thread.__init__(self) - - def run(self): - qr_code = pyqrcode.create(self.qr_content) - qr_stream = BytesIO() - qr_code.png(qr_stream, scale=5) - - email = mail.EmailMultiAlternatives( - self.subject, self.msg, from_email=None, to=[self.recipient_email] - ) - email.attach_alternative(self.html_msg, "text/html") - email.attach("code.png", self.qr_stream.getvalue(), "text/png") - - # if above code is defined directly in function, it will run synchronously - # therefore need to directly define in threading class to run asynchronously - - email.send() +from django.conf import settings +from django.core.mail import EmailMultiAlternatives def send_creation_email(app: Application) -> None: """ @@ -59,38 +29,56 @@ def send_creation_email(app: Application) -> None: # send_html_email is threaded from the User class # see user/models.py - app.user.send_html_email(template_name, context, subject) + app.user.send_html_email.delay(template_name, context, subject) - -def send_confirmation_email(app: Application) -> None: +@shared_task +def send_confirmation_email(app_id: int) -> None: """ Sends a confirmation email to a user, which contains their QR code as well as additional event information. - :param app: The user's application - :type app: Application + :param app_id: The ID of the user's application + :type app_id: int :return: None """ - subject = f"TAMUhack: Important Day-Of Information" - email_template = "application/emails/confirmed.html" - context = { - "first_name": app.first_name, - "event_name": settings.EVENT_NAME, - "organizer_name": settings.ORGANIZER_NAME, - "event_year": settings.EVENT_YEAR, - "organizer_email": settings.ORGANIZER_EMAIL, - "apple_wallet_url": get_apple_wallet_pass_url(app.user.email), - "event_date_text": settings.EVENT_DATE_TEXT, - } - html_msg = render_to_string(email_template, context) - plain_msg = html.strip_tags(html_msg) - - qr_content = json.dumps( - { + try: + app = Application.objects.get(id=app_id) + subject = f"TAMUhack: Important Day-Of Information" + email_template = "application/emails/confirmed.html" + + if app.status == "E": + subject = f"TAMUhack Waitlist: Important Day-of Information!" + email_template = "application/emails/confirmed-waitlist.html" + + context = { "first_name": app.first_name, - "last_name": app.last_name, - "email": app.user.email, - "university": app.school.name, + "event_name": settings.EVENT_NAME, + "organizer_name": settings.ORGANIZER_NAME, + "event_year": settings.EVENT_YEAR, + "organizer_email": settings.ORGANIZER_EMAIL, + "apple_wallet_url": get_apple_wallet_pass_url(app.user.email), + "event_date_text": settings.EVENT_DATE_TEXT, } - ) - - email_thread = EmailQRThread(subject, plain_msg, html_msg, app.user.email, qr_content) - email_thread.start() + + html_msg = render_to_string(email_template, context) + plain_msg = html.strip_tags(html_msg) + email = mail.EmailMultiAlternatives( + subject, plain_msg, from_email=None, to=[app.user.email] + ) + + email.attach_alternative(html_msg, "text/html") + qr_content = json.dumps( + { + "first_name": app.first_name, + "last_name": app.last_name, + "email": app.user.email, + "university": app.school.name, + } + ) + + qr_code = pyqrcode.create(qr_content) + qr_stream = BytesIO() + qr_code.png(qr_stream, scale=5) + email.attach("code.png", qr_stream.getvalue(), "image/png") + print(f"sending confirmation email to {app.user.email}") + email.send() + except Exception as e: + print(f"Error sending confirmation email: {e}") \ No newline at end of file diff --git a/hiss/hiss/__init__.py b/hiss/hiss/__init__.py index e69de29b..e0b971ee 100644 --- a/hiss/hiss/__init__.py +++ b/hiss/hiss/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import, unicode_literals +from .celery import app as celery_app + +__all__ = ('celery_app',) \ No newline at end of file diff --git a/hiss/hiss/celery.py b/hiss/hiss/celery.py new file mode 100644 index 00000000..af82effb --- /dev/null +++ b/hiss/hiss/celery.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import, unicode_literals +import os + +from celery import Celery + +# Set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hiss.settings.dev') + +app = Celery('hiss') + +# Using a string here so the worker doesn't have to serialize the object to child processes. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks() + +# Celery configuration for Redis as the broker and result backend +app.conf.broker_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') +app.conf.result_backend = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') + +@app.task(bind=True) +def debug_task(self): + print(f'Request: {self.request!r}') diff --git a/hiss/hiss/settings/dev.py b/hiss/hiss/settings/dev.py index 2d3fd32a..829bfe3c 100644 --- a/hiss/hiss/settings/dev.py +++ b/hiss/hiss/settings/dev.py @@ -4,6 +4,8 @@ # noinspection PyUnresolvedReferences from .customization import * +import os + SECRET_KEY = "development" DEBUG = True # Database @@ -25,3 +27,11 @@ MEDIA_ROOT = "resumes" AWS_S3_KEY_PREFIX = "dev-resumes" + +CELERY_ACCEPT_CONTENT = ['json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = 'UTC' +CELERY_ENABLE_UTC = True +CELERY_BROKER_URL = os.environ.get('REDIS_URL') +CELERY_RESULT_BACKEND = os.environ.get('REDIS_URL') \ No newline at end of file diff --git a/hiss/requirements.txt b/hiss/requirements.txt index ed3d495d..650e2c59 100644 --- a/hiss/requirements.txt +++ b/hiss/requirements.txt @@ -54,3 +54,5 @@ typed-ast==1.4.0 urllib3==1.25.6 whitenoise==5.2.0 wrapt==1.11.2 +celery==4.4.7 +redis \ No newline at end of file diff --git a/hiss/shared/admin_functions.py b/hiss/shared/admin_functions.py index 85f1de6e..0185a2ad 100644 --- a/hiss/shared/admin_functions.py +++ b/hiss/shared/admin_functions.py @@ -1,46 +1,19 @@ +from celery import shared_task from django.core.mail import get_connection, EmailMultiAlternatives -import threading -#create separate Threading class for mass emails -class MassEmailThread(threading.Thread): - def __init__(self, subject, text_content, html_content, from_email, recipient_list, connection): - threading.Thread.__init__(self) - self.subject = subject - self.text_content = text_content - self.html_content = html_content - self.from_email = from_email - self.recipient_list = recipient_list - self.connection = connection - self.result = 0 - - def run(self): - email = EmailMultiAlternatives(self.subject, self.text_content, self.from_email, self.recipient_list) - email.attach_alternative(self.html_content, "text/html") - try: - self.result = email.send(fail_silently=False, connection=self.connection) - except Exception as e: - print("Error sending email: ", e) - self.result = 0 - -def send_mass_html_mail(datatuple, fail_silently=False, user=None, password=None, connection=None): +@shared_task +def send_mass_html_mail(datatuple, fail_silently=False, user=None, password=None): """ - Sends each message in datatuple (subject, text_content, html_content, from_email, recipient_list). - Returns the number of emails sent. + Celery task to send multiple HTML emails given a datatuple of + (subject, text_content, html_content, from_email, recipient_list). """ - connection = connection or get_connection( + connection = get_connection( username=user, password=password, fail_silently=fail_silently ) - - threads = [] - + messages = [] for subject, text, html, from_email, recipient in datatuple: - email_thread = MassEmailThread(subject, text, html, from_email, recipient, connection) - email_thread.start() - threads.append(email_thread) - - for thread in threads: - thread.join() - - total_sent = sum(thread.result for thread in threads if thread.result) - - return total_sent \ No newline at end of file + message = EmailMultiAlternatives(subject, text, from_email, recipient) + message.attach_alternative(html, "text/html") + messages.append(message) + + return connection.send_messages(messages) diff --git a/hiss/user/models.py b/hiss/user/models.py index c0a040fd..a1d0aefd 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -7,7 +7,11 @@ from django.template.loader import render_to_string from django.utils import html from rest_framework.authtoken.models import Token -import threading + +from celery import shared_task +from django.core.mail import send_mail +from django.template.loader import render_to_string +from django.utils.html import strip_tags class EmailUserManager(auth_models.UserManager): """ @@ -36,23 +40,6 @@ def create_superuser( extra_fields.setdefault("is_active", True) return self._create_user(email, password, **extra_fields) -class EmailThread(threading.Thread): - def __init__(self, subject, plain_message, recipient_email, html_message): - self.subject = subject - self.plain_message = plain_message - self.recipient_email = recipient_email - self.html_message = html_message - threading.Thread.__init__(self) - - def run(self): - mail.send_mail( - subject=self.subject, - message=self.plain_message, - from_email= settings.ORGANIZER_EMAIL, - recipient_list=[self.recipient_email], - html_message=self.html_message, - ) - class User(auth_models.AbstractUser): """ A representation of a user within the registration system. Users are uniquely identified by their email, @@ -87,12 +74,18 @@ class User(auth_models.AbstractUser): USERNAME_FIELD = "email" REQUIRED_FIELDS = [] - def send_html_email(self, template_name, context, subject): - """Send an HTML email to the user.""" + @shared_task + def send_html_email(template_name, context, subject, recipient_email): + """Celery task to send an HTML email.""" html_msg = render_to_string(template_name, context) - plain_msg = html.strip_tags(html_msg) - email_thread = EmailThread(subject, plain_msg, self.email, html_msg) - email_thread.start() + plain_msg = strip_tags(html_msg) + send_mail( + subject=subject, + message=plain_msg, + from_email=settings.ORGANIZER_EMAIL, + recipient_list=[recipient_email], + html_message=html_msg, + ) @receiver(post_save, sender=settings.AUTH_USER_MODEL) From 63c3d294b8fe823942f5913a82987d734fd55967 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 01:44:00 -0600 Subject: [PATCH 04/17] update allowed hosts again --- hiss/hiss/settings/deployment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiss/hiss/settings/deployment.py b/hiss/hiss/settings/deployment.py index b6762692..fb98ab8f 100644 --- a/hiss/hiss/settings/deployment.py +++ b/hiss/hiss/settings/deployment.py @@ -7,7 +7,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False -ALLOWED_HOSTS = ["register.tamuhack.com", 'localhost', '127.0.0.1'] +ALLOWED_HOSTS = ['*',"https://register.tamuhack.com/","http://register.tamuhack.com/","register.tamuhack.com/","register.tamuhack.com", 'localhost', '127.0.0.1'] SESSION_COOKIE_SECURE = False SECURE_BROWSER_XSS_FILTER = False CSRF_COOKIE_SECURE = False From 407ac806feed4eada9f46d890fc76339325cf9f5 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 01:46:06 -0600 Subject: [PATCH 05/17] add allowed hosts to base.py as well --- hiss/hiss/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiss/hiss/settings/base.py b/hiss/hiss/settings/base.py index cec92994..9cabdec6 100644 --- a/hiss/hiss/settings/base.py +++ b/hiss/hiss/settings/base.py @@ -118,7 +118,7 @@ AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY") AWS_S3_BUCKET_NAME = "2025-th-resumes" AWS_S3_KEY_PREFIX = "prod" - +ALLOWED_HOSTS = ['*',"https://register.tamuhack.com/","http://register.tamuhack.com/","register.tamuhack.com/","register.tamuhack.com", 'localhost', '127.0.0.1'] STATIC_URL = "/" + BASE_PATHNAME + "static/" STATICFILES_DIRS = [os.path.join(BASE_DIR, "..", "static/")] STATIC_ROOT = "public/" From 1908a09b170304ce491e46801328e0127ef08304 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 01:57:11 -0600 Subject: [PATCH 06/17] add recipient email to function call when creation email is sent --- hiss/application/emails.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 052c8dcb..073f6a4c 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -29,7 +29,7 @@ def send_creation_email(app: Application) -> None: # send_html_email is threaded from the User class # see user/models.py - app.user.send_html_email.delay(template_name, context, subject) + app.user.send_html_email.delay(template_name, context, subject,app.user.email) @shared_task def send_confirmation_email(app_id: int) -> None: From 29a30b24a67b1207edfa1444d41bdfafda1a436c Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 02:15:08 -0600 Subject: [PATCH 07/17] add SSL requirement for celery and change send html email function to user email_user instead of send_mail --- hiss/application/emails.py | 2 +- hiss/hiss/celery.py | 11 ++++++++--- hiss/user/models.py | 10 ++-------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 073f6a4c..052c8dcb 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -29,7 +29,7 @@ def send_creation_email(app: Application) -> None: # send_html_email is threaded from the User class # see user/models.py - app.user.send_html_email.delay(template_name, context, subject,app.user.email) + app.user.send_html_email.delay(template_name, context, subject) @shared_task def send_confirmation_email(app_id: int) -> None: diff --git a/hiss/hiss/celery.py b/hiss/hiss/celery.py index af82effb..f12402ef 100644 --- a/hiss/hiss/celery.py +++ b/hiss/hiss/celery.py @@ -11,12 +11,17 @@ # Using a string here so the worker doesn't have to serialize the object to child processes. app.config_from_object('django.conf:settings', namespace='CELERY') -# Load task modules from all registered Django app configs. -app.autodiscover_tasks() - # Celery configuration for Redis as the broker and result backend app.conf.broker_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') app.conf.result_backend = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') +app.conf.update( + broker_use_ssl={ + 'ssl_cert_reqs': 'CERT_REQUIRED', + }, +) + +# Load task modules from all registered Django app configs. +app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): diff --git a/hiss/user/models.py b/hiss/user/models.py index a1d0aefd..93e262b4 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -75,17 +75,11 @@ class User(auth_models.AbstractUser): REQUIRED_FIELDS = [] @shared_task - def send_html_email(template_name, context, subject, recipient_email): + def send_html_email(self, template_name, context, subject): """Celery task to send an HTML email.""" html_msg = render_to_string(template_name, context) plain_msg = strip_tags(html_msg) - send_mail( - subject=subject, - message=plain_msg, - from_email=settings.ORGANIZER_EMAIL, - recipient_list=[recipient_email], - html_message=html_msg, - ) + self.email_user(subject, plain_msg, None, html_message=html_msg) @receiver(post_save, sender=settings.AUTH_USER_MODEL) From 91a4be82a985029697a3f20bacda616d888f2345 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 02:34:45 -0600 Subject: [PATCH 08/17] celery tasks are standalone so they do not recognize self in function calls --- hiss/application/emails.py | 2 +- hiss/user/models.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 052c8dcb..4c8306d4 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -29,7 +29,7 @@ def send_creation_email(app: Application) -> None: # send_html_email is threaded from the User class # see user/models.py - app.user.send_html_email.delay(template_name, context, subject) + app.user.send_html_email.delay(template_name, context, subject, app.user.email) @shared_task def send_confirmation_email(app_id: int) -> None: diff --git a/hiss/user/models.py b/hiss/user/models.py index 93e262b4..a1d0aefd 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -75,11 +75,17 @@ class User(auth_models.AbstractUser): REQUIRED_FIELDS = [] @shared_task - def send_html_email(self, template_name, context, subject): + def send_html_email(template_name, context, subject, recipient_email): """Celery task to send an HTML email.""" html_msg = render_to_string(template_name, context) plain_msg = strip_tags(html_msg) - self.email_user(subject, plain_msg, None, html_message=html_msg) + send_mail( + subject=subject, + message=plain_msg, + from_email=settings.ORGANIZER_EMAIL, + recipient_list=[recipient_email], + html_message=html_msg, + ) @receiver(post_save, sender=settings.AUTH_USER_MODEL) From ab7b22e1bf6589e5fe812b5b71ac06d7501942f9 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 02:44:53 -0600 Subject: [PATCH 09/17] add required SSL configuration to celery app --- hiss/hiss/celery.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hiss/hiss/celery.py b/hiss/hiss/celery.py index f12402ef..3fa21110 100644 --- a/hiss/hiss/celery.py +++ b/hiss/hiss/celery.py @@ -1,12 +1,19 @@ from __future__ import absolute_import, unicode_literals import os - +import ssl from celery import Celery # Set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hiss.settings.dev') -app = Celery('hiss') +app = Celery('hiss', + broker_use_ssl={ + 'ssl_cert_reqs': ssl.CERT_REQUIRED, + }, + redis_backend_use_ssl = { + 'ssl_cert_reqs': ssl.CERT_REQUIRED, + } + ) # Using a string here so the worker doesn't have to serialize the object to child processes. app.config_from_object('django.conf:settings', namespace='CELERY') @@ -14,12 +21,6 @@ # Celery configuration for Redis as the broker and result backend app.conf.broker_url = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') app.conf.result_backend = os.environ.get('REDIS_URL', 'redis://localhost:6379/0') -app.conf.update( - broker_use_ssl={ - 'ssl_cert_reqs': 'CERT_REQUIRED', - }, -) - # Load task modules from all registered Django app configs. app.autodiscover_tasks() From cfe3e2dbcbefb3152f6aae8e0efd299e8ec6f73c Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 02:54:26 -0600 Subject: [PATCH 10/17] change SSL requirement --- hiss/hiss/celery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hiss/hiss/celery.py b/hiss/hiss/celery.py index 3fa21110..141f3128 100644 --- a/hiss/hiss/celery.py +++ b/hiss/hiss/celery.py @@ -8,10 +8,10 @@ app = Celery('hiss', broker_use_ssl={ - 'ssl_cert_reqs': ssl.CERT_REQUIRED, + 'ssl_cert_reqs': ssl.CERT_NONE, }, redis_backend_use_ssl = { - 'ssl_cert_reqs': ssl.CERT_REQUIRED, + 'ssl_cert_reqs': ssl.CERT_NONE, } ) From 58ac1ad4c15cda9f18551265887f5a6f46b66edb Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 12:14:08 -0600 Subject: [PATCH 11/17] move Procfile to root --- hiss/Procfile => Procfile | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename hiss/Procfile => Procfile (100%) diff --git a/hiss/Procfile b/Procfile similarity index 100% rename from hiss/Procfile rename to Procfile From bad37607378481a7baf1537f67663709020af3fe Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 12:38:18 -0600 Subject: [PATCH 12/17] revert creation email to synchronous sending and update Procfile --- Procfile | 2 +- hiss/application/emails.py | 5 +---- hiss/user/models.py | 13 +++---------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Procfile b/Procfile index d48c4bff..1a0119b7 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ web: gunicorn hiss.wsgi --log-file - -worker: celery -A hiss worker --loglevel=info \ No newline at end of file +worker: celery -A hiss worker.celery --loglevel=info \ No newline at end of file diff --git a/hiss/application/emails.py b/hiss/application/emails.py index 4c8306d4..be1c1e20 100644 --- a/hiss/application/emails.py +++ b/hiss/application/emails.py @@ -26,10 +26,7 @@ def send_creation_email(app: Application) -> None: "organizer_email": settings.ORGANIZER_EMAIL, } - # send_html_email is threaded from the User class - # see user/models.py - - app.user.send_html_email.delay(template_name, context, subject, app.user.email) + app.user.send_html_email(template_name, context, subject) @shared_task def send_confirmation_email(app_id: int) -> None: diff --git a/hiss/user/models.py b/hiss/user/models.py index a1d0aefd..f0bdb959 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -74,18 +74,11 @@ class User(auth_models.AbstractUser): USERNAME_FIELD = "email" REQUIRED_FIELDS = [] - @shared_task - def send_html_email(template_name, context, subject, recipient_email): - """Celery task to send an HTML email.""" + def send_html_email(self, template_name, context, subject): + """Send an HTML email to the user.""" html_msg = render_to_string(template_name, context) plain_msg = strip_tags(html_msg) - send_mail( - subject=subject, - message=plain_msg, - from_email=settings.ORGANIZER_EMAIL, - recipient_list=[recipient_email], - html_message=html_msg, - ) + self.email_user(subject, plain_msg, None, html_message=html_msg) @receiver(post_save, sender=settings.AUTH_USER_MODEL) From d33764a7bc8f62be23f542fb8f705319552ddeca Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 12:51:00 -0600 Subject: [PATCH 13/17] update Procfile to reference application --- Procfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Procfile b/Procfile index 1a0119b7..d3b3da20 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ -web: gunicorn hiss.wsgi --log-file - -worker: celery -A hiss worker.celery --loglevel=info \ No newline at end of file +web: gunicorn hiss.wsgi:application --log-file - +worker: celery -A hiss.celery worker --loglevel=info From b2b192ca600d08343aec47bace568bceed7ae989 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 12:56:06 -0600 Subject: [PATCH 14/17] Adjust buildpacks on Heroku From 07cc886e7de80d034605cf86384738217e54a3c2 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 13:02:35 -0600 Subject: [PATCH 15/17] Add correct Procfile with web and worker --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index d3b3da20..5a7bca36 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,2 @@ web: gunicorn hiss.wsgi:application --log-file - -worker: celery -A hiss.celery worker --loglevel=info +worker: celery -A hiss.celery worker --loglevel=info \ No newline at end of file From 6400ac4aaa40d7e90d2fdf7c11873eb5f830ae33 Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 14:35:02 -0600 Subject: [PATCH 16/17] explicitly define from email for creation email --- hiss/user/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiss/user/models.py b/hiss/user/models.py index f0bdb959..f10a3bda 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -78,7 +78,7 @@ def send_html_email(self, template_name, context, subject): """Send an HTML email to the user.""" html_msg = render_to_string(template_name, context) plain_msg = strip_tags(html_msg) - self.email_user(subject, plain_msg, None, html_message=html_msg) + self.email_user(subject, plain_msg, "hello@tamuhack.com", html_message=html_msg) @receiver(post_save, sender=settings.AUTH_USER_MODEL) From f42469942ac6efa35b729164e533a509fcc08b0b Mon Sep 17 00:00:00 2001 From: skandrigi Date: Fri, 29 Nov 2024 14:56:14 -0600 Subject: [PATCH 17/17] update from email --- hiss/user/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hiss/user/models.py b/hiss/user/models.py index f10a3bda..4c55610e 100644 --- a/hiss/user/models.py +++ b/hiss/user/models.py @@ -78,7 +78,7 @@ def send_html_email(self, template_name, context, subject): """Send an HTML email to the user.""" html_msg = render_to_string(template_name, context) plain_msg = strip_tags(html_msg) - self.email_user(subject, plain_msg, "hello@tamuhack.com", html_message=html_msg) + self.email_user(subject, plain_msg, from_email=None, html_message=html_msg) @receiver(post_save, sender=settings.AUTH_USER_MODEL)