Initial commit: WrestleDesk full project

- Django backend with DRF (clubs, wrestlers, trainers, exercises, templates, trainings, homework, locations, leistungstest)
- Next.js 16 frontend with React, Shadcn UI, Tailwind
- JWT authentication
- Full CRUD for all entities
- Calendar view for trainings
- Homework management system
- Leistungstest tracking
This commit is contained in:
Andrej Spielmann
2026-03-26 13:24:57 +01:00
commit 3fefc550fe
256 changed files with 38295 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
from rest_framework import viewsets
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django.db.models import Count, Avg, Sum, F
from django.db.models.functions import Coalesce
from datetime import datetime, timedelta
from .models import TrainingLogEntry
from .serializers import TrainingLogEntrySerializer
from wrestlers.models import Wrestler
class TrainingLogEntryViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = TrainingLogEntrySerializer
def get_queryset(self):
queryset = TrainingLogEntry.objects.all()
wrestler = self.request.query_params.get('wrestler')
exercise = self.request.query_params.get('exercise')
date_from = self.request.query_params.get('date_from')
date_to = self.request.query_params.get('date_to')
if wrestler:
queryset = queryset.filter(wrestler=wrestler)
if exercise:
queryset = queryset.filter(exercise=exercise)
if date_from:
queryset = queryset.filter(logged_at__date__gte=date_from)
if date_to:
queryset = queryset.filter(logged_at__date__lte=date_to)
return queryset.select_related('wrestler', 'exercise', 'training')
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def training_log_stats(request):
wrestler_id = request.query_params.get('wrestler')
today = datetime.now().date()
week_start = today - timedelta(days=today.weekday())
queryset = TrainingLogEntry.objects.all()
if wrestler_id:
queryset = queryset.filter(wrestler=wrestler_id)
total_entries = queryset.count()
unique_exercises = queryset.values('exercise').distinct().count()
total_reps = queryset.aggregate(total=Coalesce(Sum(F('reps') * F('sets')), 0))['total'] or 0
avg_sets = queryset.aggregate(avg=Avg('sets'))['avg'] or 0
avg_rating = queryset.aggregate(avg=Avg('rating'))['avg'] or 0
this_week = queryset.filter(logged_at__date__gte=week_start).count()
top_exercises = queryset.values('exercise__name').annotate(
count=Count('id')
).order_by('-count')[:5]
progress = {}
exercises = queryset.values('exercise', 'exercise__name').distinct()
for ex in exercises:
ex_id = ex['exercise']
entries = queryset.filter(exercise=ex_id).order_by('logged_at')
if entries.count() >= 2:
first_reps = entries.first().reps * entries.first().sets
last_reps = entries.last().reps * entries.last().sets
if first_reps > 0:
change = ((last_reps - first_reps) / first_reps) * 100
progress[ex['exercise__name']] = {
'before': first_reps,
'after': last_reps,
'change_percent': round(change, 1)
}
return Response({
'total_entries': total_entries,
'unique_exercises': unique_exercises,
'total_reps': total_reps,
'avg_sets': round(avg_sets, 1),
'avg_rating': round(avg_rating, 1),
'this_week': this_week,
'top_exercises': [{'name': e['exercise__name'], 'count': e['count']} for e in top_exercises],
'progress': progress,
})
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def training_log_compare(request):
wrestler1_id = request.query_params.get('wrestler1')
wrestler2_id = request.query_params.get('wrestler2')
if not wrestler1_id or not wrestler2_id:
return Response({'error': 'Both wrestler1 and wrestler2 required'}, status=400)
wrestler1 = Wrestler.objects.get(id=wrestler1_id)
wrestler2 = Wrestler.objects.get(id=wrestler2_id)
entries1 = TrainingLogEntry.objects.filter(wrestler=wrestler1)
entries2 = TrainingLogEntry.objects.filter(wrestler=wrestler2)
exercises1 = entries1.values('exercise', 'exercise__name').distinct()
exercises2 = entries2.values('exercise', 'exercise__name').distinct()
common_exercises = set(e['exercise'] for e in exercises1) & set(e['exercise'] for e in exercises2)
comparison = []
for ex_id in common_exercises:
ex_name = entries1.filter(exercise=ex_id).first().exercise.name
avg1 = entries1.filter(exercise=ex_id).aggregate(avg=Avg(F('reps') * F('sets')))['avg'] or 0
avg2 = entries2.filter(exercise=ex_id).aggregate(avg=Avg(F('reps') * F('sets')))['avg'] or 0
comparison.append({
'exercise': ex_name,
'wrestler1_avg': round(avg1, 1),
'wrestler2_avg': round(avg2, 1)
})
return Response({
'wrestler1': {'id': wrestler1.id, 'name': str(wrestler1)},
'wrestler2': {'id': wrestler2.id, 'name': str(wrestler2)},
'exercises': comparison
})