Skip to content

Commit

Permalink
Migrate Grants vouchers to ConferenceVoucher (#4119)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoacierno authored Oct 20, 2024
1 parent a5c1c74 commit 5d5c790
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 319 deletions.
3 changes: 3 additions & 0 deletions backend/conferences/models/conference_voucher.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.db import models
from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _
from conferences.querysets import ConferenceVoucherQuerySet
from model_utils.models import TimeStampedModel

from conferences.models import Conference
Expand Down Expand Up @@ -49,6 +50,8 @@ class VoucherType(models.TextChoices):
help_text=_("When the email was last sent"), blank=True, null=True
)

objects = ConferenceVoucherQuerySet().as_manager()

def get_voucher_configuration(self):
if self.voucher_type in (
ConferenceVoucher.VoucherType.SPEAKER,
Expand Down
7 changes: 7 additions & 0 deletions backend/conferences/querysets.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from django.db import models


class ConferenceQuerySetMixin:
@property
def conference_lookup_field(self):
Expand All @@ -8,3 +11,7 @@ def for_conference(self, conference):

def for_conference_code(self, conference):
return self.filter(**{f"{self.conference_lookup_field}__code": conference})


class ConferenceVoucherQuerySet(ConferenceQuerySetMixin, models.QuerySet):
pass
106 changes: 58 additions & 48 deletions backend/grants/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from django.db import transaction
from custom_admin.audit import (
create_addition_admin_log_entry,
create_change_admin_log_entry,
)
from conferences.models.conference_voucher import ConferenceVoucher
from pycon.constants import UTC
from custom_admin.admin import validate_single_conference_selection
from import_export.resources import ModelResource
Expand All @@ -10,17 +16,14 @@
from django.utils import timezone
from import_export.admin import ExportMixin
from import_export.fields import Field
from django.utils.crypto import get_random_string
from users.admin_mixins import ConferencePermissionMixin
from countries import countries
from grants.tasks import (
send_grant_reply_approved_email,
send_grant_reply_waiting_list_email,
send_grant_reply_waiting_list_update_email,
send_grant_reply_rejected_email,
send_grant_voucher_email,
)
from pretix import create_voucher
from schedule.models import ScheduleItem
from submissions.models import Submission
from .models import Grant
Expand Down Expand Up @@ -264,37 +267,22 @@ def send_reply_email_waiting_list_update(modeladmin, request, queryset):
messages.info(request, f"Sent Waiting List update reply email to {grant.name}")


@admin.action(description="Send voucher via email")
@validate_single_conference_selection
def send_voucher_via_email(modeladmin, request, queryset):
count = 0
for grant in queryset.filter(pretix_voucher_id__isnull=False):
send_grant_voucher_email.delay(grant_id=grant.id)
count = count + 1

messages.success(request, f"{count} Voucher emails scheduled!")


def _generate_voucher_code(prefix: str) -> str:
charset = list("ABCDEFGHKLMNPQRSTUVWXYZ23456789")
random_string = get_random_string(length=20, allowed_chars=charset)
return f"{prefix}-{random_string}"


@admin.action(description="Create grant vouchers on Pretix")
@admin.action(description="Create grant vouchers")
@validate_single_conference_selection
def create_grant_vouchers_on_pretix(modeladmin, request, queryset):
@transaction.atomic
def create_grant_vouchers(modeladmin, request, queryset):
conference = queryset.first().conference

if not conference.pretix_conference_voucher_quota_id:
messages.error(
request,
"Please configure the grant voucher quota ID in the conference settings",
existing_vouchers_by_user_id = {
voucher.user_id: voucher
for voucher in ConferenceVoucher.objects.for_conference(conference).filter(
user_id__in=queryset.values_list("user_id", flat=True),
)
return
}

vouchers_to_create = []
vouchers_to_update = []

count = 0
for grant in queryset.filter(pretix_voucher_id__isnull=True).order_by("id"):
for grant in queryset.order_by("id"):
if grant.status != Grant.Status.confirmed:
messages.error(
request,
Expand All @@ -303,24 +291,47 @@ def create_grant_vouchers_on_pretix(modeladmin, request, queryset):
)
continue

voucher_code = _generate_voucher_code("GRANT")
pretix_voucher = create_voucher(
conference=grant.conference,
code=voucher_code,
comment=f"Voucher for user_id={grant.user_id}",
tag="grants",
quota_id=grant.conference.pretix_conference_voucher_quota_id,
price_mode="set",
value="0.00",
)
existing_voucher = existing_vouchers_by_user_id.get(grant.user_id)

pretix_voucher_id = pretix_voucher["id"]
grant.pretix_voucher_id = pretix_voucher_id
grant.voucher_code = voucher_code
grant.save()
count += 1
if not existing_voucher:
create_addition_admin_log_entry(
request.user,
grant,
change_message="Created voucher for this grant",
)

vouchers_to_create.append(
ConferenceVoucher(
conference_id=grant.conference_id,
user_id=grant.user_id,
voucher_code=ConferenceVoucher.generate_code(),
voucher_type=ConferenceVoucher.VoucherType.GRANT,
)
)
continue

if existing_voucher.voucher_type == ConferenceVoucher.VoucherType.CO_SPEAKER:
messages.warning(
request,
f"Grant for {grant.name} already has a Co-Speaker voucher. Upgrading to a Grant voucher.",
)
create_change_admin_log_entry(
request.user,
existing_voucher,
change_message="Upgraded Co-Speaker voucher to Grant voucher",
)
create_change_admin_log_entry(
request.user,
grant,
change_message="Updated existing Co-Speaker voucher to grant",
)
existing_voucher.voucher_type = ConferenceVoucher.VoucherType.GRANT
vouchers_to_update.append(existing_voucher)

ConferenceVoucher.objects.bulk_create(vouchers_to_create, ignore_conflicts=True)
ConferenceVoucher.objects.bulk_update(vouchers_to_update, ["voucher_type"])

messages.success(request, f"{count} Vouchers created on Pretix!")
messages.success(request, "Vouchers created!")


@admin.action(description="Mark grants as Rejected and send email")
Expand Down Expand Up @@ -457,8 +468,7 @@ class GrantAdmin(ExportMixin, ConferencePermissionMixin, admin.ModelAdmin):
send_reply_emails,
send_grant_reminder_to_waiting_for_confirmation,
send_reply_email_waiting_list_update,
create_grant_vouchers_on_pretix,
send_voucher_via_email,
create_grant_vouchers,
mark_rejected_and_send_email,
"delete_selected",
]
Expand Down
28 changes: 0 additions & 28 deletions backend/grants/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,34 +129,6 @@ def notify_new_grant_reply_slack(*, grant_id, admin_url):
)


@app.task
def send_grant_voucher_email(*, grant_id):
grant = Grant.objects.get(id=grant_id)

user = grant.user
voucher_code = grant.voucher_code

conference = grant.conference
conference_name = grant.conference.name.localize("en")

email_template = EmailTemplate.objects.for_conference(conference).get_by_identifier(
EmailTemplateIdentifier.grant_voucher_code
)
email_template.send_email(
recipient=user,
placeholders={
"user_name": get_name(user, "there"),
"voucher_code": voucher_code,
"has_approved_accommodation": grant.has_approved_accommodation(),
"conference_name": conference_name,
"visa_page_link": urljoin(settings.FRONTEND_URL, "/visa"),
},
)

grant.voucher_email_sent_at = timezone.now()
grant.save()


def _send_grant_waiting_list_email(grant_id, template_identifier):
grant = Grant.objects.get(id=grant_id)
reply_url = urljoin(settings.FRONTEND_URL, "/grants/reply/")
Expand Down
Loading

0 comments on commit 5d5c790

Please sign in to comment.