Files
guilan-ace-backend/apps/events/admin.py

181 lines
8.0 KiB
Python

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from django.utils import timezone
from core.admin import BaseModelAdmin, SoftDeleteListFilter
from core.templatetags.jalali import jdate
from apps.events.models import Event, Registration
from apps.events.resources import EventResource, RegistrationResource
from apps.events.services import verify_registration_attendance
@admin.register(Event)
class EventAdmin(BaseModelAdmin, ImportExportModelAdmin):
resource_class = EventResource
list_display = (
'title', 'event_type', 'start_time_display', 'end_time_display', 'status',
'price_display', 'capacity_display', 'attendees_display', 'is_registration_open_display',
)
list_filter = (
'event_type', 'status', 'is_deleted', 'start_time', 'end_time',
'registration_start_date', 'registration_end_date', SoftDeleteListFilter,
)
search_fields = ('title', 'description', 'address')
prepopulated_fields = {'slug': ('title',)}
date_hierarchy = 'start_time'
filter_horizontal = ('gallery_images',)
fieldsets = (
('Event Details', {'fields': ('title', 'slug', 'description', 'featured_image')}),
('Timing & Type', {'fields': ('start_time', 'end_time', 'event_type', 'status')}),
('Location & Online', {
'fields': ('address', 'location', 'online_link'),
'description': 'For On-Site or Hybrid events, provide address and select on map. For Online events, provide a link.',
}),
('Registration & Pricing', {
'fields': ('capacity', 'price', 'registration_start_date', 'registration_end_date', 'registration_success_markdown'),
'description': 'Leave capacity blank for unlimited. Leave price blank for free events.',
}),
('Gallery', {'fields': ('gallery_images',), 'description': 'Add images related to this event from the Gallery app.'}),
('Soft Delete', {'fields': ('is_deleted', 'deleted_at'), 'classes': ('collapse',)}),
)
readonly_fields = ('deleted_at',)
actions = BaseModelAdmin.actions + [
'make_published', 'make_draft', 'make_cancelled', 'make_completed', 'restore_events',
]
@admin.display(description='Price')
def price_display(self, obj):
return obj.price if obj.price is not None else 'رایگان'
@admin.display(description='Start')
def start_time_display(self, obj):
return jdate(obj.start_time)
@admin.display(description='End')
def end_time_display(self, obj):
return jdate(obj.end_time)
@admin.display(description='Capacity')
def capacity_display(self, obj):
return obj.capacity if obj.capacity is not None else 'نامحدود'
@admin.display(description='Attendees')
def attendees_display(self, obj):
return obj.current_attendees_count
@admin.display(description='Open', boolean=True)
def is_registration_open_display(self, obj):
return obj.is_registration_open
@admin.action(description='Mark selected events as published')
def make_published(self, request, queryset):
queryset.update(status=Event.StatusChoices.PUBLISHED)
self.message_user(request, f'Published {queryset.count()} events.')
@admin.action(description='Mark selected events as draft')
def make_draft(self, request, queryset):
queryset.update(status=Event.StatusChoices.DRAFT)
self.message_user(request, f'Marked {queryset.count()} events as draft.')
@admin.action(description='Mark selected events as cancelled')
def make_cancelled(self, request, queryset):
queryset.update(status=Event.StatusChoices.CANCELLED)
self.message_user(request, f'Cancelled {queryset.count()} events.')
@admin.action(description='Mark selected events as completed')
def make_completed(self, request, queryset):
queryset.update(status=Event.StatusChoices.COMPLETED)
self.message_user(request, f'Completed {queryset.count()} events.')
@admin.action(description='Restore selected events')
def restore_events(self, request, queryset):
for event in queryset:
event.restore()
self.message_user(request, f'Restored {queryset.count()} events.')
@admin.register(Registration)
class RegistrationAdmin(BaseModelAdmin, ImportExportModelAdmin):
resource_class = RegistrationResource
list_display = (
'user', 'event', 'status', 'registered_at', 'ticket_id',
'discount_code', 'discount_amount', 'final_price',
)
list_filter = ('status', 'event', 'is_deleted', 'registered_at', SoftDeleteListFilter)
search_fields = (
'user__username', 'user__email', 'user__first_name', 'user__last_name',
'event__title', 'ticket_id',
)
readonly_fields = (
'ticket_id', 'registered_at', 'confirmation_notification_sent_at',
'cancellation_notification_sent_at', 'discount_code', 'discount_amount',
'final_price', 'deleted_at', 'attendance_verified_at',
'attendance_verified_by', 'attendance_verification_source',
)
fieldsets = (
('Registration Details', {'fields': (
'user', 'event', 'status', 'registered_at', 'ticket_id',
'confirmation_notification_sent_at', 'cancellation_notification_sent_at',
)}),
('Attendance verification', {'fields': (
'attendance_verified_at', 'attendance_verified_by', 'attendance_verification_source',
)}),
('Pricing & Discount', {
'fields': ('discount_code', 'discount_amount', 'final_price'),
'classes': ('collapse',),
}),
('Soft Delete', {'fields': ('is_deleted', 'deleted_at'), 'classes': ('collapse',)}),
)
actions = BaseModelAdmin.actions + [
'confirm_registrations', 'cancel_registrations', 'mark_attended', 'restore_registrations',
]
@admin.action(description='Confirm selected registrations')
def confirm_registrations(self, request, queryset):
count = 0
for registration in queryset:
registration.status = Registration.StatusChoices.CONFIRMED
registration.save(update_fields=['status'])
count += 1
self.message_user(request, f'Confirmed {count} registrations.')
@admin.action(description='Cancel selected registrations')
def cancel_registrations(self, request, queryset):
count = 0
for registration in queryset:
registration.status = Registration.StatusChoices.CANCELLED
registration.save(update_fields=['status'])
count += 1
self.message_user(request, f'Cancelled {count} registrations.')
@admin.action(description='Mark selected registrations as attended')
def mark_attended(self, request, queryset):
count = 0
for registration in queryset:
verify_registration_attendance(
registration,
verifier=request.user,
source=Registration.AttendanceVerificationSource.STAFF_ADMIN,
)
count += 1
self.message_user(request, f'Marked {count} registrations as attended.')
def save_model(self, request, obj, form, change):
previous_status = None
if change and obj.pk:
previous_status = Registration.all_objects.filter(pk=obj.pk).values_list("status", flat=True).first()
if (
obj.status == Registration.StatusChoices.ATTENDED
and (previous_status != Registration.StatusChoices.ATTENDED or not obj.attendance_verified_at)
):
obj.attendance_verified_by = request.user
obj.attendance_verified_at = timezone.now()
obj.attendance_verification_source = Registration.AttendanceVerificationSource.STAFF_ADMIN
super().save_model(request, obj, form, change)
@admin.action(description='Restore selected registrations')
def restore_registrations(self, request, queryset):
for registration in queryset:
registration.restore()
self.message_user(request, f'Restored {queryset.count()} registrations.')