Source code for coalition.endorsements.admin

from typing import Any

from django.contrib import admin
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils import timezone
from django.utils.html import format_html

from .email_service import EndorsementEmailService
from .models import Endorsement


@admin.register(Endorsement)
[docs] class EndorsementAdmin(admin.ModelAdmin):
[docs] list_display = ( "stakeholder_name", "stakeholder_organization", "endorsement_type", "campaign", "status_badge", "email_verified_badge", "public_display", "display_publicly", "created_at", "reviewed_by", )
[docs] list_filter = ( "status", "email_verified", "public_display", "display_publicly", "created_at", "campaign", "stakeholder__type", "stakeholder__state", )
[docs] search_fields = ( "stakeholder__first_name", "stakeholder__last_name", "stakeholder__organization", "stakeholder__email", "campaign__title", "statement", )
[docs] raw_id_fields = ("stakeholder", "campaign", "reviewed_by")
[docs] ordering = ("-created_at",)
[docs] readonly_fields = ( "verification_token", "verification_sent_at", "verified_at", "terms_accepted", "terms_accepted_at", "org_authorized", "created_at", "updated_at", "verification_link", )
[docs] fieldsets = ( ( "Endorsement Details", { "fields": ( "stakeholder", "campaign", "statement", "public_display", ), }, ), ( "Email Verification", { "fields": ( "email_verified", "verification_token", "verification_link", "verification_sent_at", "verified_at", ), }, ), ( "Admin Review", { "fields": ( "status", "display_publicly", "admin_notes", "reviewed_by", "reviewed_at", ), }, ), ( "Terms & Authorization", { "fields": ( "terms_accepted", "terms_accepted_at", "org_authorized", ), "classes": ("collapse",), }, ), ( "Timestamps", { "fields": ( "created_at", "updated_at", ), "classes": ("collapse",), }, ), )
[docs] actions = [ "approve_endorsements", "reject_endorsements", "mark_verified", "send_verification_emails", "send_approval_notifications", "approve_for_display", "remove_from_display", ]
[docs] def stakeholder_name(self, obj: Endorsement) -> str: return obj.stakeholder.name
stakeholder_name.short_description = "Name" stakeholder_name.admin_order_field = "stakeholder__last_name"
[docs] def stakeholder_organization(self, obj: Endorsement) -> str: return obj.stakeholder.organization
stakeholder_organization.short_description = "Organization" stakeholder_organization.admin_order_field = "stakeholder__organization"
[docs] def endorsement_type(self, obj: Endorsement) -> str: if not obj.stakeholder.organization: return "Individual" elif obj.org_authorized: return f"On behalf of {obj.stakeholder.organization}" else: return f"Individual (affiliated with {obj.stakeholder.organization})"
endorsement_type.short_description = "Endorsement Type"
[docs] def status_badge(self, obj: Endorsement) -> str: colors = { "pending": "#ffc107", # warning yellow "verified": "#17a2b8", # info blue "approved": "#28a745", # success green "rejected": "#dc3545", # danger red } color = colors.get(obj.status, "#6c757d") return format_html( '<span style="background-color: {}; color: white; padding: 2px 8px; ' 'border-radius: 3px; font-size: 11px;">{}</span>', color, obj.get_status_display(), )
status_badge.short_description = "Status" status_badge.admin_order_field = "status"
[docs] def email_verified_badge(self, obj: Endorsement) -> str: if obj.email_verified: return format_html('<span style="color: #28a745;">✓ Verified</span>') else: return format_html('<span style="color: #dc3545;">✗ Unverified</span>')
email_verified_badge.short_description = "Email" email_verified_badge.admin_order_field = "email_verified" verification_link.short_description = "Verification Link"
[docs] def approve_endorsements( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = 0 for endorsement in queryset: if endorsement.status != "approved": endorsement.approve(user=request.user) # Send approval notification EndorsementEmailService.send_confirmation_email(endorsement) count += 1 self.message_user( request, f"Successfully approved {count} endorsement(s) and sent notifications.", )
approve_endorsements.short_description = "Approve selected endorsements"
[docs] def reject_endorsements( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = 0 for endorsement in queryset: if endorsement.status != "rejected": endorsement.reject(user=request.user) count += 1 self.message_user(request, f"Successfully rejected {count} endorsement(s).")
reject_endorsements.short_description = "Reject selected endorsements"
[docs] def mark_verified( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = 0 for endorsement in queryset: if not endorsement.email_verified: endorsement.verify_email() count += 1 self.message_user( request, f"Successfully marked {count} endorsement(s) as email verified.", )
mark_verified.short_description = "Mark as email verified"
[docs] def send_verification_emails( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = 0 for endorsement in queryset: if ( not endorsement.email_verified and EndorsementEmailService.send_verification_email(endorsement) ): count += 1 self.message_user( request, f"Successfully sent verification emails for {count} endorsement(s).", )
send_verification_emails.short_description = "Send verification emails"
[docs] def send_approval_notifications( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = 0 for endorsement in queryset.filter(status="approved"): if EndorsementEmailService.send_confirmation_email(endorsement): count += 1 self.message_user( request, f"Successfully sent approval notifications for {count} endorsement(s).", )
send_approval_notifications.short_description = "Send approval notifications"
[docs] def approve_for_display( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: # Only approve endorsements that meet all requirements count = queryset.filter( status="approved", email_verified=True, public_display=True, ).update(display_publicly=True) self.message_user( request, f"Successfully approved {count} endorsement(s) for public display.", )
approve_for_display.short_description = "Approve for public display"
[docs] def remove_from_display( self, request: HttpRequest, queryset: QuerySet[Endorsement], ) -> None: count = queryset.update(display_publicly=False) self.message_user( request, f"Successfully removed {count} endorsement(s) from public display.", )
[docs] def save_model( self, request: HttpRequest, obj: Endorsement, form: Any, change: bool, ) -> None: # Track who made changes if change and "status" in form.changed_data: obj.reviewed_by = request.user obj.reviewed_at = timezone.now() super().save_model(request, obj, form, change)