from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.utils import timezone from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION from django.utils.html import escape from django.urls import reverse from django.utils.safestring import mark_safe from .models import Club, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, Court, DateInterval, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer, Image from .forms import CustomUserCreationForm, CustomUserChangeForm from .filters import TeamScoreTournamentListFilter, MatchTournamentListFilter, SimpleTournamentListFilter, MatchTypeListFilter, SimpleIndexListFilter, StartDateRangeFilter from sync.admin import SyncedObjectAdmin class CustomUserAdmin(UserAdmin): form = CustomUserChangeForm add_form = CustomUserCreationForm model = CustomUser search_fields = ['username', 'email', 'phone', 'first_name', 'last_name', 'licence_id'] list_display = ['email', 'first_name', 'last_name', 'username', 'date_joined', 'latest_event_club_name', 'is_active', 'event_count', 'origin', 'registration_payment_mode', 'licence_id'] list_filter = ['is_active', 'origin'] ordering = ['-date_joined'] raw_id_fields = ['agents'] fieldsets = [ (None, {'fields': ['id', 'username', 'email', 'password', 'first_name', 'last_name', 'is_active', 'registration_payment_mode', 'clubs', 'country', 'phone', 'licence_id', 'umpire_code', 'summons_message_body', 'summons_message_signature', 'summons_available_payment_methods', 'summons_display_format', 'summons_display_entry_fee', 'summons_use_full_custom_message', 'match_formats_default_duration', 'bracket_match_format_preference', 'group_stage_match_format_preference', 'loser_bracket_match_format_preference', 'device_id', 'loser_bracket_mode', 'groups', 'origin', 'agents', 'should_synchronize' ]}), ] add_fieldsets = [ ( None, { "classes": ["wide"], "fields": ['username', 'email', 'password1', 'password2', 'first_name', 'last_name', 'clubs', 'country', 'phone', 'licence_id', 'umpire_code', 'groups'], }, ), ] def save_model(self, request, obj, form, change): obj.last_update = timezone.now() super().save_model(request, obj, form, change) class EventAdmin(SyncedObjectAdmin): list_display = ['creation_date', 'name', 'club', 'creator', 'creator_full_name', 'tenup_id', 'display_images'] list_filter = ['creator', 'tenup_id'] raw_id_fields = ['creator'] ordering = ['-creation_date'] readonly_fields = ['display_images_preview'] fieldsets = [ (None, {'fields': ['name', 'club', 'creator', 'creation_date', 'tenup_id']}), ('Images', {'fields': ['display_images_preview'], 'classes': ['collapse']}), ] def display_images(self, obj): count = obj.images.count() return count if count > 0 else '-' display_images.short_description = 'Images' def display_images_preview(self, obj): html = '
' for image in obj.images.all(): html += f'''

{image.title or "Untitled"}
Type: {image.get_image_type_display()}

''' html += '
' if not obj.images.exists(): html = '

No images uploaded for this event.

' return mark_safe(html) display_images_preview.short_description = 'Images Preview' class TournamentAdmin(SyncedObjectAdmin): list_display = ['display_name', 'event', 'is_private', 'start_date', 'payment', 'creator', 'is_canceled'] list_filter = [StartDateRangeFilter, 'is_deleted', 'event__creator'] ordering = ['-start_date'] search_fields = ['id'] class TeamRegistrationAdmin(SyncedObjectAdmin): list_display = ['player_names', 'group_stage', 'name', 'tournament', 'registration_date'] list_filter = [SimpleTournamentListFilter] search_fields = ['id'] class TeamScoreAdmin(SyncedObjectAdmin): list_display = ['team_registration', 'score', 'walk_out', 'match'] list_filter = [TeamScoreTournamentListFilter] search_fields = ['id'] raw_id_fields = ['team_registration', 'match'] # Add this line list_per_page = 50 # Controls pagination on the list view def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('team_registration', 'match') class RoundAdmin(SyncedObjectAdmin): list_display = ['tournament', 'name', 'parent', 'index'] list_filter = [SimpleTournamentListFilter, SimpleIndexListFilter] search_fields = ['id'] ordering = ['parent', 'index'] raw_id_fields = ['parent'] # Add this line list_per_page = 50 # Controls pagination on the list view def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('parent') class PlayerRegistrationAdmin(SyncedObjectAdmin): list_display = ['first_name', 'last_name', 'licence_id', 'rank'] search_fields = ['id', 'first_name', 'last_name', 'licence_id__icontains'] list_filter = ['registered_online', TeamScoreTournamentListFilter] ordering = ['last_name', 'first_name'] raw_id_fields = ['team_registration'] # Add this line list_per_page = 50 # Controls pagination on the list view def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('team_registration') class MatchAdmin(SyncedObjectAdmin): list_display = ['__str__', 'round', 'group_stage', 'start_date', 'end_date', 'index'] list_filter = [MatchTypeListFilter, MatchTournamentListFilter, SimpleIndexListFilter] ordering = ['-group_stage', 'round', 'index'] raw_id_fields = ['round', 'group_stage'] # Add this line list_per_page = 50 # Controls pagination on the list view def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('round', 'group_stage') class GroupStageAdmin(SyncedObjectAdmin): list_display = ['tournament', 'start_date', 'index'] list_filter = [SimpleTournamentListFilter] ordering = ['-start_date', 'index'] class ClubAdmin(SyncedObjectAdmin): list_display = ['name', 'acronym', 'city', 'creator', 'events_count', 'broadcast_code'] search_fields = ['name', 'acronym', 'city'] ordering = ['creator'] raw_id_fields = ['creator', 'related_user'] class PurchaseAdmin(SyncedObjectAdmin): list_display = ['id', 'user', 'product_id', 'quantity', 'purchase_date', 'revocation_date', 'expiration_date'] list_filter = ['user'] ordering = ['-purchase_date'] class CourtAdmin(SyncedObjectAdmin): list_display = ['index', 'name', 'club'] ordering = ['club'] class DateIntervalAdmin(SyncedObjectAdmin): list_display = ['court_index', 'event'] class FailedApiCallAdmin(admin.ModelAdmin): list_display = ['date', 'user', 'type', 'error'] list_filter = ['user'] class LogAdmin(admin.ModelAdmin): list_display = ['date', 'user', 'message'] list_filter = ['user'] class DeviceTokenAdmin(admin.ModelAdmin): list_display = ['user', 'value'] list_filter = ['user'] class DrawLogAdmin(SyncedObjectAdmin): list_display = ['tournament', 'draw_date', 'draw_seed', 'draw_match_index', 'draw_team_position'] list_filter = [SimpleTournamentListFilter] ordering = ['draw_date'] class UnregisteredTeamAdmin(admin.ModelAdmin): list_display = ['player_names', 'tournament'] list_filter = [SimpleTournamentListFilter] class UnregisteredPlayerAdmin(admin.ModelAdmin): list_display = ['first_name', 'last_name', 'licence_id'] search_fields = ['first_name', 'last_name'] list_filter = [] ordering = ['last_name', 'first_name'] class ImageAdmin(admin.ModelAdmin): list_display = ['title', 'event', 'image_type', 'order', 'uploaded_at', 'file_size', 'image_preview_small'] list_filter = ['event', 'image_type', 'uploaded_at'] search_fields = ['title', 'description', 'event__name'] ordering = ['order'] readonly_fields = ['id', 'uploaded_at', 'image_preview', 'file_size'] raw_id_fields = ['event'] def image_preview(self, obj): if obj.image: return mark_safe(f'') return "No Image" image_preview.short_description = 'Preview' def image_preview_small(self, obj): if obj.image: return mark_safe(f'') return "No Image" image_preview_small.short_description = 'Preview' def file_size(self, obj): if obj.image and hasattr(obj.image, 'size'): # Convert bytes to KB or MB size_bytes = obj.image.size if size_bytes < 1024: return f"{size_bytes} bytes" elif size_bytes < 1024 * 1024: return f"{size_bytes/1024:.1f} KB" else: return f"{size_bytes/(1024*1024):.1f} MB" return "Unknown" file_size.short_description = 'File Size' action_flags = { ADDITION: 'Addition', CHANGE: 'Change', DELETION: 'Deletion', } @admin.register(LogEntry) class LogEntryAdmin(admin.ModelAdmin): date_hierarchy = 'action_time' list_filter = ['user', 'content_type', 'action_flag'] search_fields = ['object_repr', 'change_message'] list_display = ['action_time', 'user', 'content_type', 'object_link', 'action_flag_display', 'change_message'] readonly_fields = [field.name for field in LogEntry._meta.get_fields()] def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False def object_link(self, obj): if obj.action_flag == DELETION: link = escape(obj.object_repr) else: ct = obj.content_type try: link = '%s' % ( reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=[obj.object_id]), escape(obj.object_repr), ) except: link = escape(obj.object_repr) return mark_safe(link) object_link.short_description = 'Object' def action_flag_display(self, obj): return action_flags.get(obj.action_flag, '') action_flag_display.short_description = 'Action' admin.site.register(CustomUser, CustomUserAdmin) admin.site.register(Club, ClubAdmin) admin.site.register(Event, EventAdmin) admin.site.register(Round, RoundAdmin) admin.site.register(GroupStage, GroupStageAdmin) admin.site.register(Match, MatchAdmin) admin.site.register(TeamScore, TeamScoreAdmin) admin.site.register(TeamRegistration, TeamRegistrationAdmin) admin.site.register(Tournament, TournamentAdmin) admin.site.register(PlayerRegistration, PlayerRegistrationAdmin) admin.site.register(Purchase, PurchaseAdmin) admin.site.register(Court, CourtAdmin) admin.site.register(DateInterval, DateIntervalAdmin) admin.site.register(FailedApiCall, FailedApiCallAdmin) admin.site.register(Log, LogAdmin) admin.site.register(DeviceToken, DeviceTokenAdmin) admin.site.register(DrawLog, DrawLogAdmin) admin.site.register(UnregisteredTeam, UnregisteredTeamAdmin) admin.site.register(UnregisteredPlayer, UnregisteredPlayerAdmin) admin.site.register(Image, ImageAdmin)