Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion sponsorship/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,15 @@

from .models import SponsorshipProfile

admin.site.register(SponsorshipProfile)

@admin.register(SponsorshipProfile)
class SponsorshipProfileAdmin(admin.ModelAdmin):
list_display = (
"organization_name",
"main_contact_user",
"sponsorship_type",
"sponsorship_tier",
"application_status",
)
list_filter = ("sponsorship_type", "application_status", "sponsorship_tier")
search_fields = ("organization_name", "main_contact_user")
9 changes: 9 additions & 0 deletions sponsorship/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.apps import AppConfig


class SponsorshipConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "sponsorship"

def ready(self):
import sponsorship.signals # noqa: this registers the signals
46 changes: 46 additions & 0 deletions sponsorship/emails.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.conf import settings
from django.core.mail import send_mail
from django.template.loader import render_to_string


def send_sponsorship_status_emails(profile):
user = profile.user

# Email to sponsor
sponsor_subject = "Your Sponsorship Profile Has Been Approved"
sponsor_message = render_to_string(
"sponsorship/email/sponsor_status_update.txt",
{"user": user, "profile": profile},
)
send_mail(
sponsor_subject, sponsor_message, settings.DEFAULT_FROM_EMAIL, [user.email]
)

# Email to internal team (hardcoded for now)
team_subject = f"New Sponsorship Approved: {profile.organization_name}"
team_message = render_to_string(
"sponsorship/email/team_status_notification.txt",
{"user": user, "profile": profile},
)
send_mail(
team_subject,
team_message,
settings.DEFAULT_FROM_EMAIL,
["[email protected]"], # Replace with actual team emails later
)


def send_sponsorship_profile_email(user, profile, is_update=False):
subject = "Sponsorship Profile Submission Received"
message = render_to_string(
"sponsorship/email/sponsor_status_update.txt",
{"user": user, "profile": profile, "is_update": is_update},
)

send_mail(
subject,
message,
settings.DEFAULT_FROM_EMAIL,
[user.email],
fail_silently=False,
)
28 changes: 27 additions & 1 deletion sponsorship/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,35 @@ class Meta:
model = SponsorshipProfile
fields = [
"main_contact_user",
"additional_contacts",
"organization_name",
"sponsorship_type",
"sponsorship_tier",
"logo",
"company_description",
"application_status",
]
widgets = {
"company_description": forms.Textarea(
attrs={
"rows": 4,
}
),
}

def __init__(self, *args, **kwargs):
user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)

self._user = user # ✅ Save user for later use

if user:
self.fields["main_contact_user"].initial = user

def save(self, commit=True):
instance = super().save(commit=False)
instance.main_contact_user = self._user # Enforce value
instance.application_status = "pending" # Set status manually
if commit:
instance.save()
self.save_m2m()
return instance
30 changes: 28 additions & 2 deletions sponsorship/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.2.3 on 2025-07-23 22:44
# Generated by Django 5.1.7 on 2025-08-19 05:52

import django.db.models.deletion
from django.conf import settings
Expand All @@ -14,6 +14,23 @@ class Migration(migrations.Migration):
]

operations = [
migrations.CreateModel(
name="SponsorshipTier",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("amount", models.DecimalField(decimal_places=2, max_digits=10)),
("name", models.CharField(max_length=100)),
("description", models.TextField()),
],
),
migrations.CreateModel(
name="SponsorshipProfile",
fields=[
Expand Down Expand Up @@ -73,7 +90,7 @@ class Migration(migrations.Migration):
"main_contact_user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="main_contact",
related_name="main_contact_user",
to=settings.AUTH_USER_MODEL,
),
),
Expand All @@ -85,6 +102,15 @@ class Migration(migrations.Migration):
to=settings.AUTH_USER_MODEL,
),
),
(
"sponsorship_tier",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="sponsorship.sponsorshiptier",
),
),
],
),
]
15 changes: 9 additions & 6 deletions sponsorship/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ class SponsorshipProfile(models.Model):
User, on_delete=models.CASCADE, related_name="sponsorship_user"
)
main_contact_user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="main_contact"
User, on_delete=models.CASCADE, related_name="main_contact_user"
)
additional_contacts = models.ManyToManyField(
User, blank=True, related_name="additional_contacts"
)
organization_name = models.CharField(max_length=255)
sponsorship_type = models.CharField(max_length=20, choices=SPONSORSHIP_TYPES)
# sponsorship_tier = models.ForeignKey("SponsorshipTier", on_delete=models.SET_NULL, null=True, blank = True)
sponsorship_tier = models.ForeignKey(
"SponsorshipTier", on_delete=models.SET_NULL, null=True, blank=True
)
logo = models.ImageField(upload_to="sponsor_logos/", null=True, blank=True)
company_description = models.TextField()
application_status = models.CharField(
Expand All @@ -48,10 +50,11 @@ def __str__(self):
return self.organization_name


# class SponsorshipTier(models.Model):
# amount = models.DecimalField(max_digits=10, decimal_places=2)
# name = models.CharField(max_length=100)
# description = models.TextField()
class SponsorshipTier(models.Model):
amount = models.DecimalField(max_digits=10, decimal_places=2)
name = models.CharField(max_length=100)
description = models.TextField()


# def __str__(self):
# return self.name
66 changes: 66 additions & 0 deletions sponsorship/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.mail import EmailMultiAlternatives
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.template.loader import render_to_string

from .models import SponsorshipProfile


def _send_email(
subject, recipient_list, *, html_template=None, text_template=None, context=None
):
context = context or {}
context["current_site"] = Site.objects.get_current()

text_content = render_to_string(text_template, context)
html_content = render_to_string(html_template, context)

msg = EmailMultiAlternatives(
subject,
text_content,
settings.DEFAULT_FROM_EMAIL,
recipient_list,
)
msg.attach_alternative(html_content, "text/html")
msg.send()


@receiver(post_save, sender=SponsorshipProfile)
def sponsorship_profile_signal(sender, instance, created, **kwargs):
"""Send emails when sponsorship profile is submitted or approved."""
if created:
# Email on submission
subject = (
f"{settings.ACCOUNT_EMAIL_SUBJECT_PREFIX} Sponsorship Application Received"
)
_send_email(
subject,
[instance.user.email],
html_template="sponsorship/email/sponsor_status_update.html",
text_template="sponsorship/email/sponsor_status_update.txt",
context={"profile": instance},
)
elif instance.application_status == "approved":
# Email on approval
subject = (
f"{settings.ACCOUNT_EMAIL_SUBJECT_PREFIX} Sponsorship Profile Approved"
)
_send_email(
subject,
[instance.user.email],
html_template="sponsorship/email/sponsor_approved.html",
text_template="sponsorship/email/sponsor_approved.txt",
context={"profile": instance},
)

# Internal team notification
internal_subject = f"{settings.ACCOUNT_EMAIL_SUBJECT_PREFIX} New Sponsorship Approved: {instance.organization_name}"
_send_email(
internal_subject,
["[email protected]"], # Replace with real internal emails later
html_template="sponsorship/email/team_status_notification.html",
text_template="sponsorship/email/team_status_notification.txt",
context={"profile": instance},
)
14 changes: 14 additions & 0 deletions sponsorship/templates/sponsorship/email/sponsor_approved.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<p>
Hi {{ profile.user.first_name }},
</p>
<p>
Congratulations! Your sponsorship application for {{ profile.organization_name }} has been approved 🎉
</p>
<p>
We're excited to have you as a sponsor for PyLadiesCon. A team member will reach out soon with next steps and onboarding information.
</p>
<p>
Best,
<br>
The PyLadiesCon Team
</p>
8 changes: 8 additions & 0 deletions sponsorship/templates/sponsorship/email/sponsor_approved.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hi {{ profile.user.first_name }},

Congratulations! Your sponsorship application for {{ profile.organization_name }} has been approved 🎉

We're excited to have you as a sponsor for PyLadiesCon. A team member will reach out soon with next steps and onboarding information.

Best,
The PyLadiesCon Team
14 changes: 14 additions & 0 deletions sponsorship/templates/sponsorship/email/sponsor_status_update.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<p>
Hi {{ profile.user.first_name }},
</p>
<p>
Thank you for submitting your sponsorship application for {{ profile.organization_name }}!
</p>
<p>
We’ve received your profile and our team will review it shortly. You will receive a follow-up email once a decision has been made.
</p>
<p>
Best,
<br>
The PyLadiesCon Team
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hi {{ profile.user.first_name }},

Thank you for submitting your sponsorship application for {{ profile.organization_name }}!

We’ve received your profile and our team will review it shortly. You will receive a follow-up email once a decision has been made.

Best,
The PyLadiesCon Team
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<p>
Hello team,
</p>
<p>
The sponsorship profile for <strong>{{ profile.organization_name }}</strong> has been approved.
</p>
<p>
Please review the profile and begin the sponsor onboarding process.
</p>
<p>
View profile: (Add internal link if available)
</p>
<p>
Thanks,
<br>
The System
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hello team,

The sponsorship profile for {{ profile.organization_name }} has been approved.

Please review the profile and begin the sponsor onboarding process.

Thanks,
The System
1 change: 1 addition & 0 deletions sponsorship/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
path(
"create/", views.create_sponsorship_profile, name="create_sponsorship_profile"
),
path("success/", views.sponsorship_success, name="success"),
]
Loading
Loading