3fefc550fe
- 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
121 lines
4.8 KiB
Python
121 lines
4.8 KiB
Python
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
|
|
})
|