Feat: Implementiere Team-Statistiken für Unterteams und Saisonauswahl
Erweitert die Team-Statistikansicht, sodass Head Coaches von übergeordneten Teams auch die Statistiken ihrer untergeordneten Teams einsehen können. Jedes Team wird mit seinen eigenen Statistiken separat dargestellt. Zusätzlich wurde eine Filterfunktion implementiert, die es ermöglicht, die Statistiken nach Saison zu filtern.
This commit is contained in:
parent
9cfe5e50c4
commit
c63ad532b5
@ -4,67 +4,109 @@
|
||||
<div class="container mt-4">
|
||||
<h1 class="mb-4">Statistics for {{ team.name }}</h1>
|
||||
|
||||
<!-- W-L Record, PCT, and Streak -->
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">Record</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ wins }} - {{ losses }}</h5>
|
||||
<p class="card-text">PCT: {{ pct|floatformat:3 }}</p>
|
||||
<p class="card-text">Streak: {{ streak }}</p>
|
||||
<!-- Season Filter Form -->
|
||||
<div class="mb-4">
|
||||
<form method="get" action="">
|
||||
<div class="row align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label for="season" class="form-label">Select Season:</label>
|
||||
<select name="season" id="season" class="form-select">
|
||||
<option value="">All Time</option>
|
||||
{% for season in available_seasons %}
|
||||
<option value="{{ season }}" {% if season|stringformat:"s" == selected_season %}selected{% endif %}>
|
||||
{{ season }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">Offense vs Defense</div>
|
||||
<div class="card-body">
|
||||
<p>Runs Scored: {{ runs_scored }}</p>
|
||||
<p>Runs Allowed: {{ runs_allowed }}</p>
|
||||
<div class="progress" style="height: 30px;">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {% widthratio runs_scored runs_scored|add:runs_allowed 100 %}%;" aria-valuenow="{{ runs_scored }}" aria-valuemin="0" aria-valuemax="{{ runs_scored|add:runs_allowed }}">{{ runs_scored }}</div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {% widthratio runs_allowed runs_scored|add:runs_allowed 100 %}%;" aria-valuenow="{{ runs_allowed }}" aria-valuemin="0" aria-valuemax="{{ runs_scored|add:runs_allowed }}">{{ runs_allowed }}</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% for stats in teams_stats %}
|
||||
{% if not forloop.first %}
|
||||
<hr class="my-4">
|
||||
{% endif %}
|
||||
|
||||
<h2 class="mb-3">{{ stats.team.name }}</h2>
|
||||
|
||||
{% if stats.total_games > 0 %}
|
||||
<!-- W-L Record, PCT, and Streak -->
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">Record</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ stats.wins }} - {{ stats.losses }}</h5>
|
||||
<p class="card-text">PCT: {{ stats.pct|floatformat:3 }}</p>
|
||||
<p class="card-text">Streak: {{ stats.streak }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">Offense vs Defense</div>
|
||||
<div class="card-body">
|
||||
<p>Runs Scored: {{ stats.runs_scored }}</p>
|
||||
<p>Runs Allowed: {{ stats.runs_allowed }}</p>
|
||||
{% with total_runs=stats.runs_scored|add:stats.runs_allowed %}
|
||||
<div class="progress" style="height: 30px;">
|
||||
{% if total_runs > 0 %}
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {% widthratio stats.runs_scored total_runs 100 %}%;" aria-valuenow="{{ stats.runs_scored }}" aria-valuemin="0" aria-valuemax="{{ total_runs }}">{{ stats.runs_scored }}</div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {% widthratio stats.runs_allowed total_runs 100 %}%;" aria-valuenow="{{ stats.runs_allowed }}" aria-valuemin="0" aria-valuemax="{{ total_runs }}">{{ stats.runs_allowed }}</div>
|
||||
{% else %}
|
||||
<div class="progress-bar" role="progressbar" style="width: 100%;">0</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Luck-O-Meter and Inning Heatmap -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">Luck-O-Meter</div>
|
||||
<div class="card-body">
|
||||
<p>Real Winning %: {{ pct|floatformat:3 }}</p>
|
||||
<p>Pythagorean Winning %: {{ pythagorean_pct|floatformat:3 }}</p>
|
||||
<!-- Luck-O-Meter and Inning Heatmap -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">Luck-O-Meter</div>
|
||||
<div class="card-body">
|
||||
<p>Real Winning %: {{ stats.pct|floatformat:3 }}</p>
|
||||
<p>Pythagorean Winning %: {{ stats.pythagorean_pct|floatformat:3 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">Inning Heatmap (Runs Scored)</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for inning, runs in stats.inning_runs.items %}
|
||||
<th>{{ inning }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for inning, runs in stats.inning_runs.items %}
|
||||
<td>{{ runs }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">Inning Heatmap (Runs Scored)</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for inning, runs in inning_runs.items %}
|
||||
<th>{{ inning }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for inning, runs in inning_runs.items %}
|
||||
<td>{{ runs }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info" role="alert">
|
||||
No games played for this team{% if selected_season %} in the {{ selected_season }} season{% endif %}.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -2,17 +2,17 @@ from django.shortcuts import render, get_object_or_404
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseForbidden
|
||||
from clubs.models import Team
|
||||
from calendars.models import Game, GameResult
|
||||
from calendars.models import Game
|
||||
|
||||
@login_required
|
||||
def team_statistics(request, team_id):
|
||||
team = get_object_or_404(Team, pk=team_id)
|
||||
|
||||
# Check if the user is the head coach of the team
|
||||
if request.user != team.head_coach:
|
||||
return HttpResponseForbidden("You are not authorized to view this page.")
|
||||
|
||||
games = Game.objects.filter(team=team, result__isnull=False).order_by('start_time')
|
||||
def _calculate_statistics(team, season=None):
|
||||
"""
|
||||
Calculates statistics for a given team and season.
|
||||
"""
|
||||
games_query = Game.objects.filter(team=team, result__isnull=False)
|
||||
if season:
|
||||
games_query = games_query.filter(season=season)
|
||||
|
||||
games = games_query.order_by('start_time')
|
||||
|
||||
wins = 0
|
||||
losses = 0
|
||||
@ -25,16 +25,13 @@ def team_statistics(request, team_id):
|
||||
|
||||
for game in games:
|
||||
result = game.result
|
||||
print("DEBUGGER:"+str(result))
|
||||
print("INNING_RESULTS:"+str(result.inning_results))
|
||||
|
||||
sorted_items = sorted(result.inning_results.items(),key=lambda x: int(x[0].split('_')[1]))
|
||||
sorted_items = sorted(result.inning_results.items(), key=lambda x: int(x[0].split('_')[1]))
|
||||
home_innings = [
|
||||
item[1].get('home')
|
||||
for item in sorted_items
|
||||
if isinstance(item[1].get('home'), int)
|
||||
]
|
||||
sorted_items = sorted(result.inning_results.items(),key=lambda x: int(x[0].split('_')[1]))
|
||||
away_innings = [
|
||||
item[1].get('guest')
|
||||
for item in sorted_items
|
||||
@ -42,25 +39,17 @@ def team_statistics(request, team_id):
|
||||
]
|
||||
home_score = sum(home_innings)
|
||||
away_score = sum(away_innings)
|
||||
print("HOMEINNING:"+str(home_innings))
|
||||
|
||||
if game.is_home_game:
|
||||
print("HEIMSPIEL")
|
||||
team_score = home_score
|
||||
opponent_score = away_score
|
||||
print("Teamscore"+str(team_score))
|
||||
print("Gegnerscore"+str(opponent_score))
|
||||
else:
|
||||
print("Auswerts")
|
||||
team_score = away_score
|
||||
opponent_score = home_score
|
||||
print("Teamscore"+str(team_score))
|
||||
print("Gegnerscore"+str(opponent_score))
|
||||
|
||||
runs_scored += team_score
|
||||
runs_allowed += opponent_score
|
||||
|
||||
|
||||
# W-L Record and Streak
|
||||
if team_score > opponent_score:
|
||||
wins += 1
|
||||
if last_game_result == 'win':
|
||||
@ -75,19 +64,15 @@ def team_statistics(request, team_id):
|
||||
else:
|
||||
streak_counter = 1
|
||||
last_game_result = 'loss'
|
||||
print("Win:"+str(wins))
|
||||
print("Looses:"+str(losses))
|
||||
# Inning Heatmap
|
||||
|
||||
team_innings = home_innings if game.is_home_game else away_innings
|
||||
for i, runs in enumerate(team_innings):
|
||||
if i + 1 in inning_runs:
|
||||
inning_runs[i + 1] += runs
|
||||
|
||||
# Winning Percentage (PCT)
|
||||
total_games = wins + losses
|
||||
pct = (wins / total_games) * 100 if total_games > 0 else 0
|
||||
|
||||
# Streak
|
||||
if last_game_result == 'win':
|
||||
streak_str = f"Won {streak_counter}"
|
||||
elif last_game_result == 'loss':
|
||||
@ -95,14 +80,12 @@ def team_statistics(request, team_id):
|
||||
else:
|
||||
streak_str = "N/A"
|
||||
|
||||
# Pythagorean Winning Percentage
|
||||
if runs_scored > 0 or runs_allowed > 0:
|
||||
pythagorean_pct = (runs_scored**2 / (runs_scored**2 + runs_allowed**2)) * 100
|
||||
else:
|
||||
pythagorean_pct = 0
|
||||
|
||||
|
||||
context = {
|
||||
return {
|
||||
'team': team,
|
||||
'wins': wins,
|
||||
'losses': losses,
|
||||
@ -112,7 +95,36 @@ def team_statistics(request, team_id):
|
||||
'runs_allowed': runs_allowed,
|
||||
'pythagorean_pct': pythagorean_pct,
|
||||
'inning_runs': inning_runs,
|
||||
'total_games': total_games,
|
||||
}
|
||||
|
||||
@login_required
|
||||
def team_statistics(request, team_id):
|
||||
team = get_object_or_404(Team, pk=team_id)
|
||||
|
||||
# User must be the head coach of the parent team to view the stats
|
||||
if request.user != team.head_coach:
|
||||
return HttpResponseForbidden("You are not authorized to view this page.")
|
||||
|
||||
selected_season = request.GET.get('season')
|
||||
|
||||
# Create a list of the parent team and all its child teams
|
||||
teams_to_display = [team] + list(team.child_teams.all())
|
||||
|
||||
# Calculate statistics for each team
|
||||
teams_stats = []
|
||||
for t in teams_to_display:
|
||||
stats = _calculate_statistics(t, selected_season)
|
||||
teams_stats.append(stats)
|
||||
|
||||
# Get all available seasons for the filter dropdown
|
||||
available_seasons = Game.objects.values_list('season', flat=True).distinct().order_by('season')
|
||||
|
||||
context = {
|
||||
'team': team, # Primary team for page header/auth
|
||||
'teams_stats': teams_stats,
|
||||
'available_seasons': available_seasons,
|
||||
'selected_season': selected_season,
|
||||
}
|
||||
print("DebuggeR:"+str(context))
|
||||
|
||||
return render(request, 'team_stats/team_statistics.html', context)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user