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, path # Add path import from django.utils.safestring import mark_safe from django.shortcuts import render # Add this import from django.db.models import Sum, Count, Avg, Q # Add these imports from datetime import datetime, timedelta # Add this import 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'] 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 = '
{image.title or "Untitled"}
Type: {image.get_image_type_display()}
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'] def dashboard_view(self, request): """Tournament dashboard view with comprehensive statistics""" # Calculate date ranges now = timezone.now() today = now.date() week_ago = today - timedelta(days=7) month_ago = today - timedelta(days=30) # Tournament statistics - running today tournaments_today = Tournament.objects.filter( start_date__date__lte=today, end_date__date__gte=today ).exclude(is_deleted=True) tournaments_today_private = tournaments_today.filter(is_private=True).count() tournaments_today_public = tournaments_today.filter(is_private=False).count() tournaments_today_total = tournaments_today.count() # All time tournament statistics all_tournaments = Tournament.objects.exclude(is_deleted=True) tournaments_all_total = all_tournaments.count() # Team and player statistics total_teams = TeamRegistration.objects.count() total_players = PlayerRegistration.objects.count() # Match statistics total_matches = Match.objects.count() matches_played = Match.objects.filter(end_date__isnull=False).count() context = { 'tournaments_today_total': tournaments_today_total, 'tournaments_today_private': tournaments_today_private, 'tournaments_today_public': tournaments_today_public, 'tournaments_all_total': tournaments_all_total, 'total_teams': total_teams, 'total_players': total_players, 'total_matches': total_matches, 'matches_played': matches_played, } return render(request, 'admin/tournaments/dashboard.html', context) def get_urls(self): urls = super().get_urls() custom_urls = [ path('dashboard/', self.admin_site.admin_view(self.dashboard_view), name='tournaments_tournament_dashboard'), ] return custom_urls + urls 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'