Feat: Saison-Report für Head Coaches
Implementiert einen druckbaren Saison-Report für Head Coaches, der eine Übersicht über die Spielerteilnahme an allen Spielen einer ausgewählten Saison bietet. Wesentliche Änderungen: - **Neue URL und View (`season_report` in `team_stats/views.py`):** Empfängt `team_id` und `season`, holt Spieler, Spiele und deren Teilnahmestatus und bereitet die Daten auf. Enthält eine Berechtigungsprüfung für den Head Coach. - **Neue Vorlage (`team_stats/templates/team_stats/season_report.html`):** Zeigt eine Tabelle mit Spielen als Zeilen und Spielern als Spalten an. Teilnahmestatus wird mit Symbolen (✔, ✖, ?) dargestellt. Enthält druckspezifisches CSS, um die Lesbarkeit und das Seitenlayout für DIN A4 (Querformat) zu optimieren, inklusive vertikal gedrehter Spielernamen in der Kopfzeile zur Platzersparnis. - **Integration (`team_statistics.html`):** Ein "Saison-Report generieren"-Button wurde zur Team-Statistikseite hinzugefügt, der den Report für die aktuell ausgewählte Saison öffnet.
This commit is contained in:
parent
d45fc54280
commit
5cc9b387b9
107
team_stats/templates/team_stats/season_report.html
Normal file
107
team_stats/templates/team_stats/season_report.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Season Report for {{ team.name }} - {{ season }}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
thead th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.player-header {
|
||||
height: 150px;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
.player-header > div {
|
||||
transform: rotate(-90deg);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
transform-origin: 0 100%;
|
||||
width: 150px; /* Should match height */
|
||||
}
|
||||
.status-attending {
|
||||
color: green;
|
||||
}
|
||||
.status-rejected {
|
||||
color: red;
|
||||
}
|
||||
.status-maybe {
|
||||
color: #aaa;
|
||||
}
|
||||
.print-button {
|
||||
margin: 1rem;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@page {
|
||||
size: A4 landscape;
|
||||
margin: 1cm;
|
||||
}
|
||||
body {
|
||||
font-size: 9px;
|
||||
}
|
||||
.print-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="window.print();" class="print-button">Print Report</button>
|
||||
|
||||
<h2>Season Report</h2>
|
||||
<p><strong>Team:</strong> {{ team.name }}</p>
|
||||
<p><strong>Season:</strong> {{ season }}</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Opponent</th>
|
||||
{% for player in players %}
|
||||
<th class="player-header"><div>{{ player.get_full_name }}</div></th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in report_data %}
|
||||
<tr>
|
||||
<td>{{ row.game.start_time|date:"d.m.Y" }}</td>
|
||||
<td>{{ row.game.opponent }}</td>
|
||||
{% for status in row.statuses %}
|
||||
<td class="status-{{ status }}">
|
||||
{% if status == 'attending' %}
|
||||
✔
|
||||
{% elif status == 'rejected' %}
|
||||
✖
|
||||
{% else %}
|
||||
?
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="{{ players|length|add:2 }}">No games found for this season.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -22,6 +22,11 @@
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
</div>
|
||||
{% if selected_season %}
|
||||
<div class="col-md-3">
|
||||
<a href="{% url 'team_stats:season_report' team_id=team.id season=selected_season %}" class="btn btn-secondary" target="_blank">Generate Season Report</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -5,4 +5,5 @@ app_name = 'team_stats'
|
||||
|
||||
urlpatterns = [
|
||||
path('team/<int:team_id>/', views.team_statistics, name='team_statistics'),
|
||||
path('team/<int:team_id>/report/<str:season>/', views.season_report, name='season_report'),
|
||||
]
|
||||
|
||||
@ -147,3 +147,50 @@ def team_statistics(request, team_id):
|
||||
}
|
||||
|
||||
return render(request, 'team_stats/team_statistics.html', context)
|
||||
|
||||
@login_required
|
||||
def season_report(request, team_id, season):
|
||||
team = get_object_or_404(Team, pk=team_id)
|
||||
|
||||
# Security check: only head coach can view
|
||||
if request.user != team.head_coach:
|
||||
return HttpResponseForbidden("You are not authorized to view this report.")
|
||||
|
||||
# 1. Get all players for the team, ordered
|
||||
players = team.players.all().order_by('last_name', 'first_name')
|
||||
player_ids = [p.id for p in players]
|
||||
|
||||
# 2. Get all games for the team and season
|
||||
games = Game.objects.filter(team=team, season=season).order_by('start_time')
|
||||
game_ids = [g.id for g in games]
|
||||
|
||||
# 3. Get all relevant participation data in one query
|
||||
participations = EventParticipation.objects.filter(
|
||||
event_id__in=game_ids,
|
||||
user_id__in=player_ids
|
||||
)
|
||||
|
||||
# 4. Create a fast lookup map
|
||||
participation_map = {
|
||||
(p.event_id, p.user_id): p.status for p in participations
|
||||
}
|
||||
|
||||
# 5. Build the final data structure for the template
|
||||
report_data = []
|
||||
for game in games:
|
||||
statuses = []
|
||||
for player in players:
|
||||
status = participation_map.get((game.id, player.id), 'maybe')
|
||||
statuses.append(status)
|
||||
report_data.append({
|
||||
'game': game,
|
||||
'statuses': statuses
|
||||
})
|
||||
|
||||
context = {
|
||||
'team': team,
|
||||
'season': season,
|
||||
'players': players,
|
||||
'report_data': report_data,
|
||||
}
|
||||
return render(request, 'team_stats/season_report.html', context)
|
||||
Loading…
x
Reference in New Issue
Block a user