add tournaments dashboard

sync3
Razmig Sarkissian 5 months ago
parent a641fcced4
commit d541205f22
  1. 56
      tournaments/admin.py
  2. 286
      tournaments/templates/admin/tournaments/dashboard.html
  3. 18
      tournaments/templates/admin/tournaments/tournament/change_list.html

@ -3,8 +3,11 @@ 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.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
@ -85,6 +88,57 @@ class TournamentAdmin(SyncedObjectAdmin):
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]

@ -0,0 +1,286 @@
{% extends "admin/base_site.html" %}
{% load admin_urls %}
{% block title %}Tournament Dashboard{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">Home</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label='tournaments' %}">Tournaments</a>
&rsaquo; Dashboard
</div>
{% endblock %}
{% block content %}
<div class="tournament-dashboard">
<h1>🏆 Tournament Dashboard</h1>
<!-- Summary Statistics Cards -->
<div class="dashboard-stats" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin: 20px 0;">
<!-- Running Tournaments Card -->
<div class="stat-card" style="background: linear-gradient(135deg, #28a745, #20c997); color: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 20px 0; display: flex; align-items: center; gap: 10px;">
🎾 Running Tournaments
</h3>
<div class="tournament-stats">
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<div>
<div style="font-size: 28px; font-weight: bold;">{{ tournaments_today_total }}</div>
<div style="opacity: 0.9; font-size: 14px;">Today</div>
</div>
<div style="text-align: right;">
<div style="font-size: 16px;">{{ tournaments_today_private }}/{{ tournaments_today_public }}</div>
<div style="opacity: 0.9; font-size: 12px;">Private/Public</div>
</div>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<div>
<div style="font-size: 24px; font-weight: bold;">{{ tournaments_week_total }}</div>
<div style="opacity: 0.9; font-size: 14px;">This Week</div>
</div>
<div style="text-align: right;">
<div style="font-size: 16px;">{{ tournaments_week_private }}/{{ tournaments_week_public }}</div>
<div style="opacity: 0.9; font-size: 12px;">Private/Public</div>
</div>
</div>
<div style="display: flex; justify-content: space-between;">
<div>
<div style="font-size: 24px; font-weight: bold;">{{ tournaments_month_total }}</div>
<div style="opacity: 0.9; font-size: 14px;">This Month</div>
</div>
<div style="text-align: right;">
<div style="font-size: 16px;">{{ tournaments_month_private }}/{{ tournaments_month_public }}</div>
<div style="opacity: 0.9; font-size: 12px;">Private/Public</div>
</div>
</div>
</div>
</div>
<!-- Ended Tournaments Card -->
<div class="stat-card" style="background: linear-gradient(135deg, #dc3545, #e74c3c); color: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 20px 0; display: flex; align-items: center; gap: 10px;">
🏁 Ended Tournaments
</h3>
<div class="tournament-stats">
<div style="margin-bottom: 15px;">
<div style="font-size: 28px; font-weight: bold;">{{ tournaments_ended_today }}</div>
<div style="opacity: 0.9; font-size: 14px;">Today</div>
</div>
<div style="margin-bottom: 15px;">
<div style="font-size: 24px; font-weight: bold;">{{ tournaments_ended_week }}</div>
<div style="opacity: 0.9; font-size: 14px;">This Week</div>
</div>
<div style="margin-bottom: 15px;">
<div style="font-size: 24px; font-weight: bold;">{{ tournaments_ended_month }}</div>
<div style="opacity: 0.9; font-size: 14px;">This Month</div>
</div>
<div>
<div style="font-size: 20px; font-weight: bold;">{{ tournaments_ended_all }}</div>
<div style="opacity: 0.9; font-size: 14px;">All Time</div>
</div>
</div>
</div>
<!-- Participants Card -->
<div class="stat-card" style="background: linear-gradient(135deg, #007bff, #0056b3); color: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 20px 0; display: flex; align-items: center; gap: 10px;">
👥 Participants
</h3>
<div class="participant-stats">
<div style="margin-bottom: 20px;">
<div style="font-size: 32px; font-weight: bold;">{{ total_teams }}</div>
<div style="opacity: 0.9; font-size: 16px;">Total Teams</div>
</div>
<div style="margin-bottom: 20px;">
<div style="font-size: 32px; font-weight: bold;">{{ total_players }}</div>
<div style="opacity: 0.9; font-size: 16px;">Total Players</div>
</div>
<div>
<div style="font-size: 20px; font-weight: bold;">{{ avg_teams_per_tournament }}</div>
<div style="opacity: 0.9; font-size: 14px;">Avg Teams/Tournament</div>
</div>
</div>
</div>
<!-- Matches Card -->
<div class="stat-card" style="background: linear-gradient(135deg, #fd7e14, #e55e2b); color: white; border-radius: 12px; padding: 25px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<h3 style="margin: 0 0 20px 0; display: flex; align-items: center; gap: 10px;">
🏓 Matches
</h3>
<div class="match-stats">
<div style="margin-bottom: 20px;">
<div style="font-size: 32px; font-weight: bold;">{{ total_matches }}</div>
<div style="opacity: 0.9; font-size: 16px;">Total Matches</div>
</div>
<div style="margin-bottom: 20px;">
<div style="font-size: 28px; font-weight: bold; color: #90EE90;">{{ matches_played }}</div>
<div style="opacity: 0.9; font-size: 14px;">Played</div>
</div>
<div>
<div style="font-size: 24px; font-weight: bold; color: #FFB6C1;">{{ matches_pending }}</div>
<div style="opacity: 0.9; font-size: 14px;">Pending</div>
</div>
</div>
</div>
</div>
<!-- All Time Overview -->
<div class="all-time-section" style="background: white; border: 1px solid #dee2e6; border-radius: 12px; padding: 25px; margin: 20px 0;">
<h3 style="margin: 0 0 20px 0; color: #495057; display: flex; align-items: center; gap: 10px;">
📊 All Time Overview
</h3>
<div class="overview-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px;">
<div class="overview-item" style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 24px; font-weight: bold; color: #28a745;">{{ tournaments_all_total }}</div>
<div style="color: #6c757d; margin-top: 5px;">Total Tournaments</div>
<div style="font-size: 12px; color: #6c757d; margin-top: 5px;">
{{ tournaments_all_private }} Private | {{ tournaments_all_public }} Public
</div>
</div>
<div class="overview-item" style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 24px; font-weight: bold; color: #007bff;">{{ tournaments_with_online_reg }}</div>
<div style="color: #6c757d; margin-top: 5px;">Online Registration</div>
</div>
<div class="overview-item" style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 24px; font-weight: bold; color: #ffc107;">{{ tournaments_with_payment }}</div>
<div style="color: #6c757d; margin-top: 5px;">Online Payment</div>
</div>
<div class="overview-item" style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 24px; font-weight: bold; color: #17a2b8;">€{{ avg_entry_fee }}</div>
<div style="color: #6c757d; margin-top: 5px;">Avg Entry Fee</div>
</div>
</div>
</div>
<!-- Detailed Breakdown Table -->
<div class="detailed-breakdown" style="background: white; border: 1px solid #dee2e6; border-radius: 12px; padding: 25px; margin-top: 20px;">
<h3 style="margin: 0 0 20px 0; color: #495057;">📈 Tournament Breakdown</h3>
<div style="overflow-x: auto;">
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background: #f8f9fa;">
<th style="padding: 15px; text-align: left; border-bottom: 2px solid #dee2e6;">Period</th>
<th style="padding: 15px; text-align: center; border-bottom: 2px solid #dee2e6;">Running</th>
<th style="padding: 15px; text-align: center; border-bottom: 2px solid #dee2e6;">Private</th>
<th style="padding: 15px; text-align: center; border-bottom: 2px solid #dee2e6;">Public</th>
<th style="padding: 15px; text-align: center; border-bottom: 2px solid #dee2e6;">Ended</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 15px; font-weight: 500;">Today</td>
<td style="padding: 15px; text-align: center;">
<span style="background: #28a745; color: white; padding: 6px 12px; border-radius: 15px; font-weight: bold;">
{{ tournaments_today_total }}
</span>
</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_today_private }}</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_today_public }}</td>
<td style="padding: 15px; text-align: center; color: #dc3545; font-weight: 500;">{{ tournaments_ended_today }}</td>
</tr>
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 15px; font-weight: 500;">This Week</td>
<td style="padding: 15px; text-align: center;">
<span style="background: #28a745; color: white; padding: 6px 12px; border-radius: 15px; font-weight: bold;">
{{ tournaments_week_total }}
</span>
</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_week_private }}</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_week_public }}</td>
<td style="padding: 15px; text-align: center; color: #dc3545; font-weight: 500;">{{ tournaments_ended_week }}</td>
</tr>
<tr style="border-bottom: 1px solid #dee2e6;">
<td style="padding: 15px; font-weight: 500;">This Month</td>
<td style="padding: 15px; text-align: center;">
<span style="background: #28a745; color: white; padding: 6px 12px; border-radius: 15px; font-weight: bold;">
{{ tournaments_month_total }}
</span>
</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_month_private }}</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_month_public }}</td>
<td style="padding: 15px; text-align: center; color: #dc3545; font-weight: 500;">{{ tournaments_ended_month }}</td>
</tr>
<tr>
<td style="padding: 15px; font-weight: 500;">All Time</td>
<td style="padding: 15px; text-align: center;">
<span style="background: #007bff; color: white; padding: 6px 12px; border-radius: 15px; font-weight: bold;">
{{ tournaments_all_total }}
</span>
</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_all_private }}</td>
<td style="padding: 15px; text-align: center; color: #6c757d;">{{ tournaments_all_public }}</td>
<td style="padding: 15px; text-align: center; color: #dc3545; font-weight: 500;">{{ tournaments_ended_all }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Quick Actions -->
<div class="quick-actions" style="background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 12px; padding: 25px; margin-top: 20px;">
<h3 style="margin: 0 0 20px 0; color: #495057;">🚀 Quick Actions</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<a href="{% url 'admin:tournaments_tournament_changelist' %}"
style="display: block; padding: 12px 15px; background: #007bff; color: white; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
View All Tournaments
</a>
<a href="{% url 'admin:tournaments_teamregistration_changelist' %}"
style="display: block; padding: 12px 15px; background: #28a745; color: white; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
Manage Teams
</a>
<a href="{% url 'admin:tournaments_playerregistration_changelist' %}"
style="display: block; padding: 12px 15px; background: #6c757d; color: white; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
Manage Players
</a>
<a href="{% url 'admin:tournaments_match_changelist' %}"
style="display: block; padding: 12px 15px; background: #fd7e14; color: white; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
View Matches
</a>
<a href="{% url 'admin:tournaments_event_changelist' %}"
style="display: block; padding: 12px 15px; background: #17a2b8; color: white; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
Manage Events
</a>
<a href="{% url 'admin:tournaments_club_changelist' %}"
style="display: block; padding: 12px 15px; background: #ffc107; color: #212529; text-decoration: none; border-radius: 8px; text-align: center; font-weight: 500;">
Manage Clubs
</a>
</div>
</div>
</div>
<style>
.tournament-dashboard {
max-width: 1400px;
margin: 0 auto;
}
.stat-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.quick-actions a:hover {
opacity: 0.9;
transform: translateY(-1px);
}
@media (max-width: 768px) {
.dashboard-stats {
grid-template-columns: 1fr !important;
}
.overview-grid {
grid-template-columns: repeat(2, 1fr) !important;
}
.quick-actions > div {
grid-template-columns: 1fr !important;
}
}
</style>
{% endblock %}

@ -0,0 +1,18 @@
{% extends "admin/change_list.html" %}
{% block object-tools %}
<ul class="object-tools">
<li>
<a href="dashboard/" class="viewlink" style="background: #007bff; color: white;">
📊 Dashboard
</a>
</li>
{% if has_add_permission %}
<li>
<a href="{% url 'admin:tournaments_tournament_add' %}" class="addlink">
Add Tournament
</a>
</li>
{% endif %}
</ul>
{% endblock %}
Loading…
Cancel
Save