From d04086ac2bffec0c0d653304d37751624e999736 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:58:24 +0000 Subject: [PATCH 1/5] Add invitation letter request flag to Grant admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display a boolean flag in the Grant admin change list view that indicates whether a request for an invitation letter has been submitted. This helps administrators quickly identify which grants have associated invitation letter requests. Changes: - Added has_invitation_letter_request() method to Grant model - Added has_invitation_letter_request_flag display method to GrantAdmin - Added flag to admin list_display with 📧 emoji icon Fixes #4384 Co-authored-by: Marco Acierno --- backend/grants/admin.py | 6 ++++++ backend/grants/models.py | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index bca135cd9e..ce099c1573 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -402,6 +402,7 @@ class GrantAdmin(ExportMixin, ConferencePermissionMixin, admin.ModelAdmin): "country", "is_proposed_speaker", "is_confirmed_speaker", + "has_invitation_letter_request_flag", "emoji_gender", "conference", "status", @@ -584,6 +585,11 @@ def user_has_ticket(self, obj: Grant) -> bool: def has_voucher(self, obj: Grant) -> bool: return obj.has_voucher + @admin.display(description="📧", boolean=True) + def has_invitation_letter_request_flag(self, obj: Grant) -> bool: + """Display flag indicating if user has submitted an invitation letter request""" + return obj.has_invitation_letter_request() + def get_queryset(self, request): qs = ( super() diff --git a/backend/grants/models.py b/backend/grants/models.py index d57384d550..50b45cc9c0 100644 --- a/backend/grants/models.py +++ b/backend/grants/models.py @@ -333,6 +333,17 @@ def has_approved_accommodation(self): def current_or_pending_status(self): return self.pending_status or self.status + def has_invitation_letter_request(self): + """Check if user has submitted an invitation letter request for this conference""" + if not self.user_id: + return False + + from visa.models import InvitationLetterRequest + return InvitationLetterRequest.objects.filter( + conference=self.conference, + requester=self.user + ).exists() + class GrantConfirmPendingStatusProxy(Grant): class Meta: From 7a2a7112b4ea8655fe4f5890dc4da6a634516e1e Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:40:26 +0000 Subject: [PATCH 2/5] Fix N+1 query issue in Grant admin invitation letter flag Use Django''s Exists annotation in get_queryset() instead of calling has_invitation_letter_request() method for each grant in the admin list. This optimizes the query to use a single SQL query with EXISTS subquery rather than individual queries for each grant row. Co-authored-by: Marco Acierno --- backend/grants/admin.py | 10 +++++++++- backend/grants/models.py | 10 ---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index ce099c1573..1a9253b97d 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -588,9 +588,11 @@ def has_voucher(self, obj: Grant) -> bool: @admin.display(description="📧", boolean=True) def has_invitation_letter_request_flag(self, obj: Grant) -> bool: """Display flag indicating if user has submitted an invitation letter request""" - return obj.has_invitation_letter_request() + return getattr(obj, 'has_invitation_letter_request', False) def get_queryset(self, request): + from visa.models import InvitationLetterRequest + qs = ( super() .get_queryset(request) @@ -614,6 +616,12 @@ def get_queryset(self, request): user_id=OuterRef("user_id"), ) ), + has_invitation_letter_request=Exists( + InvitationLetterRequest.objects.filter( + conference_id=OuterRef("conference_id"), + requester_id=OuterRef("user_id"), + ) + ), ) ) diff --git a/backend/grants/models.py b/backend/grants/models.py index 50b45cc9c0..aec77decf8 100644 --- a/backend/grants/models.py +++ b/backend/grants/models.py @@ -333,16 +333,6 @@ def has_approved_accommodation(self): def current_or_pending_status(self): return self.pending_status or self.status - def has_invitation_letter_request(self): - """Check if user has submitted an invitation letter request for this conference""" - if not self.user_id: - return False - - from visa.models import InvitationLetterRequest - return InvitationLetterRequest.objects.filter( - conference=self.conference, - requester=self.user - ).exists() class GrantConfirmPendingStatusProxy(Grant): From 0703a7b796220bbe38db3fb9a7ff434a09996e68 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Thu, 14 Aug 2025 06:22:25 +0200 Subject: [PATCH 3/5] small changes --- backend/grants/admin.py | 6 +++--- backend/grants/models.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index 1a9253b97d..641cd6c117 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -31,7 +31,7 @@ from schedule.models import ScheduleItem from submissions.models import Submission from .models import Grant, GrantConfirmPendingStatusProxy -from django.db.models import Exists, OuterRef, F +from django.db.models import Exists, OuterRef from pretix import user_has_admission_ticket from django.contrib.admin import SimpleListFilter @@ -588,11 +588,11 @@ def has_voucher(self, obj: Grant) -> bool: @admin.display(description="📧", boolean=True) def has_invitation_letter_request_flag(self, obj: Grant) -> bool: """Display flag indicating if user has submitted an invitation letter request""" - return getattr(obj, 'has_invitation_letter_request', False) + return obj.has_invitation_letter_request def get_queryset(self, request): from visa.models import InvitationLetterRequest - + qs = ( super() .get_queryset(request) diff --git a/backend/grants/models.py b/backend/grants/models.py index aec77decf8..d57384d550 100644 --- a/backend/grants/models.py +++ b/backend/grants/models.py @@ -334,7 +334,6 @@ def current_or_pending_status(self): return self.pending_status or self.status - class GrantConfirmPendingStatusProxy(Grant): class Meta: proxy = True From f80315cd951b944cb047e1b02304a9b405c3845b Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Thu, 14 Aug 2025 06:23:15 +0200 Subject: [PATCH 4/5] move import --- backend/grants/admin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index 641cd6c117..ea68fa44c3 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -38,6 +38,7 @@ from participants.models import Participant from django.urls import reverse from django.utils.safestring import mark_safe +from visa.models import InvitationLetterRequest logger = logging.getLogger(__name__) @@ -591,8 +592,6 @@ def has_invitation_letter_request_flag(self, obj: Grant) -> bool: return obj.has_invitation_letter_request def get_queryset(self, request): - from visa.models import InvitationLetterRequest - qs = ( super() .get_queryset(request) From 4ebf19012d81bc3c645c1881f2c70a67f73d8979 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Sun, 17 Aug 2025 16:00:14 +0200 Subject: [PATCH 5/5] Consistent behaviour as other flags --- backend/grants/admin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/grants/admin.py b/backend/grants/admin.py index ea68fa44c3..139e569b8b 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -403,7 +403,7 @@ class GrantAdmin(ExportMixin, ConferencePermissionMixin, admin.ModelAdmin): "country", "is_proposed_speaker", "is_confirmed_speaker", - "has_invitation_letter_request_flag", + "has_sent_invitation_letter_request", "emoji_gender", "conference", "status", @@ -586,10 +586,11 @@ def user_has_ticket(self, obj: Grant) -> bool: def has_voucher(self, obj: Grant) -> bool: return obj.has_voucher - @admin.display(description="📧", boolean=True) - def has_invitation_letter_request_flag(self, obj: Grant) -> bool: - """Display flag indicating if user has submitted an invitation letter request""" - return obj.has_invitation_letter_request + @admin.display(description="📧") + def has_sent_invitation_letter_request(self, obj: Grant) -> bool: + if obj.has_invitation_letter_request: + return "📧" + return "" def get_queryset(self, request): qs = (