You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
23 KiB
555 lines
23 KiB
from django.contrib import admin, messages
|
|
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
|
|
from django.utils.safestring import mark_safe
|
|
from django.shortcuts import render
|
|
from django.db.models import Avg
|
|
from datetime import timedelta, datetime
|
|
|
|
from biz.models import Prospect, ProspectGroup
|
|
|
|
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, UserWithEventsFilter, UserWithPurchasesFilter, UserWithProspectFilter
|
|
|
|
from sync.admin import SyncedObjectAdmin
|
|
import logging
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class CustomUserAdmin(UserAdmin):
|
|
form = CustomUserChangeForm
|
|
add_form = CustomUserCreationForm
|
|
model = CustomUser
|
|
search_fields = ['username', 'email', 'phone', 'first_name', 'last_name', 'licence_id']
|
|
filter_horizontal = ('clubs',)
|
|
actions = ['convert_to_prospect', 'create_group']
|
|
|
|
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', UserWithEventsFilter, UserWithPurchasesFilter, UserWithProspectFilter]
|
|
ordering = ['-date_joined']
|
|
autocomplete_fields = ['supervisors', 'organizers']
|
|
fieldsets = [
|
|
(None, {'fields': ['id', 'username', 'email', 'password', 'first_name', 'last_name', 'is_active', 'date_joined']}),
|
|
('Permissions', {'fields': ['is_staff', 'is_superuser', 'groups', 'user_permissions']}),
|
|
('Personal Info', {'fields': ['registration_payment_mode', 'clubs', 'country', 'phone', 'licence_id', 'umpire_code']}),
|
|
('Tournament Settings', {'fields': [
|
|
'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', 'origin', 'supervisors', 'organizers', 'should_synchronize', 'can_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)
|
|
|
|
def create_group(self, request, queryset):
|
|
prospects = []
|
|
source_value = f"auto_created_{datetime.now().strftime('%Y-%m-%d_%H:%M')}"
|
|
for user in queryset:
|
|
prospect = Prospect.objects.filter(email=user.email).first()
|
|
if prospect:
|
|
prospects.append(prospect)
|
|
else:
|
|
prospect = Prospect.objects.create(
|
|
first_name=user.first_name,
|
|
last_name=user.last_name,
|
|
email=user.email,
|
|
phone=user.phone,
|
|
official_user=user,
|
|
source=source_value
|
|
)
|
|
prospects.append(prospect)
|
|
prospect_group = ProspectGroup.objects.create(
|
|
name=f"{datetime.now().strftime('%Y-%m-%d_%H:%M')}",
|
|
)
|
|
prospect_group.prospects.add(*prospects)
|
|
messages.success(request, f'Created prospect group {prospect_group.name} with {queryset.count()} prospects')
|
|
|
|
create_group.short_description = "Create group with selection"
|
|
|
|
def convert_to_prospect(self, request, queryset):
|
|
created_count = 0
|
|
skipped_count = 0
|
|
source_value = f"user_conversion_{datetime.now().strftime('%Y-%m-%d_%H:%M')}"
|
|
|
|
for user in queryset:
|
|
if user.email and Prospect.objects.filter(email=user.email).exists():
|
|
skipped_count += 1
|
|
continue
|
|
|
|
prospect = Prospect.objects.create(
|
|
first_name=user.first_name,
|
|
last_name=user.last_name,
|
|
email=user.email,
|
|
phone=user.phone,
|
|
official_user=user,
|
|
source=source_value
|
|
)
|
|
created_count += 1
|
|
|
|
if created_count > 0:
|
|
messages.success(request, f'{created_count} prospect(s) successfully created.')
|
|
if skipped_count > 0:
|
|
messages.warning(request, f'{skipped_count} user(s) skipped (prospect with same email already exists).')
|
|
|
|
convert_to_prospect.short_description = "Convert selected users to prospects"
|
|
|
|
class EventAdmin(SyncedObjectAdmin):
|
|
list_display = ['creation_date', 'name', 'club', 'creator', 'tenup_id', 'display_images']
|
|
list_filter = ['creator', 'club', 'tenup_id']
|
|
search_fields = ['name', 'club__name', 'creator__email']
|
|
raw_id_fields = ['related_user', 'creator', 'club']
|
|
ordering = ['-creation_date']
|
|
readonly_fields = ['display_images_preview']
|
|
|
|
fieldsets = [
|
|
(None, {'fields': ['last_update', 'related_user', '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 = '<div style="display: flex; flex-wrap: wrap; gap: 10px;">'
|
|
for image in obj.images.all():
|
|
html += f'''
|
|
<div style="text-align: center; margin-bottom: 15px;">
|
|
<img src="{image.image.url}" style="max-width: 150px; max-height: 150px; object-fit: contain;" />
|
|
<p style="margin: 5px 0 0 0; font-size: 12px;">
|
|
<strong>{image.title or "Untitled"}</strong><br>
|
|
Type: {image.get_image_type_display()}<br>
|
|
</p>
|
|
</div>
|
|
'''
|
|
html += '</div>'
|
|
if not obj.images.exists():
|
|
html = '<p>No images uploaded for this event.</p>'
|
|
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', 'federal_level_category']
|
|
raw_id_fields = ['last_updated_by', 'event']
|
|
|
|
def dashboard_view(self, request):
|
|
"""Tournament dashboard view with comprehensive statistics"""
|
|
|
|
# Calculate date ranges
|
|
now = timezone.now()
|
|
today = now.date()
|
|
week_start = today - timedelta(days=today.weekday()) # Monday of this week
|
|
week_end = week_start + timedelta(days=6) # Sunday of this week
|
|
month_start = today.replace(day=1) # First day of current month
|
|
|
|
# Tournament statistics - tournaments starting TODAY
|
|
tournaments_today = Tournament.objects.filter(
|
|
start_date__date=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()
|
|
|
|
# Tournament statistics - tournaments starting THIS WEEK
|
|
tournaments_this_week = Tournament.objects.filter(
|
|
start_date__date__gte=week_start,
|
|
start_date__date__lte=week_end
|
|
).exclude(is_deleted=True)
|
|
|
|
tournaments_week_private = tournaments_this_week.filter(is_private=True).count()
|
|
tournaments_week_public = tournaments_this_week.filter(is_private=False).count()
|
|
tournaments_week_total = tournaments_this_week.count()
|
|
|
|
# Tournament statistics - tournaments starting THIS MONTH
|
|
tournaments_this_month = Tournament.objects.filter(
|
|
start_date__date__gte=month_start,
|
|
start_date__date__lte=today + timedelta(days=31 - today.day) # End of current month
|
|
).exclude(is_deleted=True)
|
|
|
|
tournaments_month_private = tournaments_this_month.filter(is_private=True).count()
|
|
tournaments_month_public = tournaments_this_month.filter(is_private=False).count()
|
|
tournaments_month_total = tournaments_this_month.count()
|
|
|
|
# All time tournament statistics
|
|
all_tournaments = Tournament.objects.exclude(is_deleted=True)
|
|
tournaments_all_private = all_tournaments.filter(is_private=True).count()
|
|
tournaments_all_public = all_tournaments.filter(is_private=False).count()
|
|
tournaments_all_total = all_tournaments.count()
|
|
|
|
# Ended tournaments (tournaments that have an end_date in the past)
|
|
tournaments_ended_today = Tournament.objects.filter(
|
|
end_date__date=today
|
|
).exclude(is_deleted=True)
|
|
|
|
tournaments_ended_week = Tournament.objects.filter(
|
|
end_date__date__gte=week_start,
|
|
end_date__date__lte=week_end
|
|
).exclude(is_deleted=True)
|
|
|
|
tournaments_ended_month = Tournament.objects.filter(
|
|
end_date__date__gte=month_start,
|
|
end_date__date__lte=today + timedelta(days=31 - today.day)
|
|
).exclude(is_deleted=True)
|
|
|
|
tournaments_ended_all = Tournament.objects.filter(
|
|
end_date__date__lt=today
|
|
).exclude(is_deleted=True)
|
|
|
|
# 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()
|
|
matches_pending = Match.objects.filter(end_date__isnull=True).count()
|
|
|
|
# Additional statistics
|
|
tournaments_with_online_reg = Tournament.objects.filter(
|
|
enable_online_registration=True
|
|
).exclude(is_deleted=True).count()
|
|
|
|
tournaments_with_payment = Tournament.objects.filter(
|
|
enable_online_payment=True
|
|
).exclude(is_deleted=True).count()
|
|
|
|
# Average statistics
|
|
avg_teams_per_tournament = TeamRegistration.objects.aggregate(
|
|
avg_teams=Avg('tournament__team_count')
|
|
)['avg_teams'] or 0
|
|
|
|
avg_entry_fee = Tournament.objects.exclude(is_deleted=True).aggregate(
|
|
avg_fee=Avg('entry_fee')
|
|
)['avg_fee'] or 0
|
|
|
|
# User Account Statistics
|
|
total_users = CustomUser.objects.count()
|
|
users_admin = CustomUser.objects.filter(origin=0).count() # ADMIN
|
|
users_site = CustomUser.objects.filter(origin=1).count() # SITE
|
|
users_app = CustomUser.objects.filter(origin=2).count() # APP
|
|
|
|
# Recent User Registrations
|
|
recent_app_users = CustomUser.objects.filter(origin=2).order_by('-date_joined')[:10]
|
|
|
|
# New users by period
|
|
users_today = CustomUser.objects.filter(date_joined__date=today).count()
|
|
users_this_week = CustomUser.objects.filter(
|
|
date_joined__date__gte=week_start,
|
|
date_joined__date__lte=week_end
|
|
).count()
|
|
users_this_month = CustomUser.objects.filter(
|
|
date_joined__date__gte=month_start,
|
|
date_joined__date__lte=today + timedelta(days=31 - today.day)
|
|
).count()
|
|
|
|
# Purchase Statistics
|
|
total_purchases = Purchase.objects.count()
|
|
recent_purchases = Purchase.objects.all().order_by('-purchase_date')[:10]
|
|
|
|
# Purchases by period
|
|
purchases_today = Purchase.objects.filter(purchase_date__date=today).count()
|
|
purchases_this_week = Purchase.objects.filter(
|
|
purchase_date__date__gte=week_start,
|
|
purchase_date__date__lte=week_end
|
|
).count()
|
|
purchases_this_month = Purchase.objects.filter(
|
|
purchase_date__date__gte=month_start,
|
|
purchase_date__date__lte=today + timedelta(days=31 - today.day)
|
|
).count()
|
|
|
|
context = {
|
|
'title': 'Tournament Dashboard',
|
|
'app_label': 'tournaments',
|
|
'opts': Tournament._meta,
|
|
|
|
# Today statistics (tournaments STARTING today)
|
|
'tournaments_today_total': tournaments_today_total,
|
|
'tournaments_today_private': tournaments_today_private,
|
|
'tournaments_today_public': tournaments_today_public,
|
|
|
|
# Week statistics (tournaments STARTING this week)
|
|
'tournaments_week_total': tournaments_week_total,
|
|
'tournaments_week_private': tournaments_week_private,
|
|
'tournaments_week_public': tournaments_week_public,
|
|
|
|
# Month statistics (tournaments STARTING this month)
|
|
'tournaments_month_total': tournaments_month_total,
|
|
'tournaments_month_private': tournaments_month_private,
|
|
'tournaments_month_public': tournaments_month_public,
|
|
|
|
# All time statistics
|
|
'tournaments_all_total': tournaments_all_total,
|
|
'tournaments_all_private': tournaments_all_private,
|
|
'tournaments_all_public': tournaments_all_public,
|
|
|
|
# Ended tournaments (tournaments ENDING in the respective periods)
|
|
'tournaments_ended_today': tournaments_ended_today.count(),
|
|
'tournaments_ended_week': tournaments_ended_week.count(),
|
|
'tournaments_ended_month': tournaments_ended_month.count(),
|
|
'tournaments_ended_all': tournaments_ended_all.count(),
|
|
|
|
# Teams and players
|
|
'total_teams': total_teams,
|
|
'total_players': total_players,
|
|
|
|
# Matches
|
|
'total_matches': total_matches,
|
|
'matches_played': matches_played,
|
|
'matches_pending': matches_pending,
|
|
|
|
# Additional stats
|
|
'tournaments_with_online_reg': tournaments_with_online_reg,
|
|
'tournaments_with_payment': tournaments_with_payment,
|
|
'avg_teams_per_tournament': round(avg_teams_per_tournament, 1),
|
|
'avg_entry_fee': round(avg_entry_fee, 2),
|
|
|
|
# User statistics
|
|
'total_users': total_users,
|
|
'users_admin': users_admin,
|
|
'users_site': users_site,
|
|
'users_app': users_app,
|
|
'users_today': users_today,
|
|
'users_this_week': users_this_week,
|
|
'users_this_month': users_this_month,
|
|
'recent_app_users': recent_app_users,
|
|
|
|
# Purchase statistics
|
|
'total_purchases': total_purchases,
|
|
'recent_purchases': recent_purchases,
|
|
'purchases_today': purchases_today,
|
|
'purchases_this_week': purchases_this_week,
|
|
'purchases_this_month': purchases_this_month,
|
|
}
|
|
|
|
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']
|
|
raw_id_fields = ['related_user', 'tournament']
|
|
|
|
class TeamScoreAdmin(SyncedObjectAdmin):
|
|
list_display = ['team_registration', 'score', 'walk_out', 'match']
|
|
list_filter = [TeamScoreTournamentListFilter]
|
|
search_fields = ['id', 'team_registration__player_registrations__first_name', 'team_registration__player_registrations__last_name']
|
|
raw_id_fields = ['team_registration', 'match']
|
|
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', 'payment_id', 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']
|
|
raw_id_fields = ['user']
|
|
|
|
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'<img src="{obj.image.url}" width="150" height="auto" style="max-height: 150px; object-fit: contain;" />')
|
|
return "No Image"
|
|
image_preview.short_description = 'Preview'
|
|
|
|
def image_preview_small(self, obj):
|
|
if obj.image:
|
|
return mark_safe(f'<img src="{obj.image.url}" width="50" height="auto" style="max-height: 50px; object-fit: contain;" />')
|
|
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 = '<a href="%s">%s</a>' % (
|
|
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)
|
|
|