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
260 lines
10 KiB
Python
260 lines
10 KiB
Python
from rest_framework import viewsets, status
|
|
from rest_framework.decorators import action
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from rest_framework.response import Response
|
|
|
|
from .models import LeistungstestTemplate, LeistungstestTemplateExercise, LeistungstestResult, LeistungstestResultItem
|
|
from .serializers import (
|
|
LeistungstestTemplateSerializer,
|
|
LeistungstestTemplateExerciseSerializer,
|
|
LeistungstestResultSerializer,
|
|
LeistungstestResultItemSerializer,
|
|
)
|
|
from .stats import get_template_leaderboard, get_exercise_leaderboard, get_used_exercises
|
|
|
|
|
|
class LeistungstestTemplateViewSet(viewsets.ModelViewSet):
|
|
queryset = LeistungstestTemplate.objects.all()
|
|
serializer_class = LeistungstestTemplateSerializer
|
|
|
|
def get_queryset(self):
|
|
return LeistungstestTemplate.objects.all().prefetch_related('exercises__exercise')
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
name = request.data.get('name')
|
|
exercises_data = request.data.get('exercises', [])
|
|
|
|
# Create template first
|
|
template = LeistungstestTemplate.objects.create(name=name)
|
|
|
|
# Create exercises
|
|
for i, ex_data in enumerate(exercises_data):
|
|
LeistungstestTemplateExercise.objects.create(
|
|
template=template,
|
|
exercise_id=ex_data['exercise'],
|
|
target_reps=ex_data['target_reps'],
|
|
order=ex_data.get('order', i),
|
|
)
|
|
|
|
# Reload with prefetch to get exercise names
|
|
template = LeistungstestTemplate.objects.prefetch_related('exercises__exercise').get(pk=template.pk)
|
|
|
|
return Response(
|
|
LeistungstestTemplateSerializer(template).data,
|
|
status=status.HTTP_201_CREATED
|
|
)
|
|
|
|
@action(detail=True, methods=['post'])
|
|
def duplicate(self, request, pk=None):
|
|
template = self.get_object()
|
|
new_template = LeistungstestTemplate.objects.create(name=f"{template.name} (Kopie)")
|
|
|
|
for exercise in template.exercises.all():
|
|
LeistungstestTemplateExercise.objects.create(
|
|
template=new_template,
|
|
exercise=exercise.exercise,
|
|
target_reps=exercise.target_reps,
|
|
order=exercise.order,
|
|
)
|
|
|
|
return Response(
|
|
LeistungstestTemplateSerializer(new_template).data,
|
|
status=status.HTTP_201_CREATED
|
|
)
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
partial = kwargs.pop('partial', False)
|
|
instance = self.get_object()
|
|
|
|
instance.name = request.data.get('name', instance.name)
|
|
instance.save()
|
|
|
|
exercises_data = request.data.get('exercises')
|
|
if exercises_data is not None:
|
|
instance.exercises.all().delete()
|
|
for i, ex_data in enumerate(exercises_data):
|
|
LeistungstestTemplateExercise.objects.create(
|
|
template=instance,
|
|
exercise_id=ex_data['exercise'],
|
|
target_reps=ex_data['target_reps'],
|
|
order=ex_data.get('order', i),
|
|
)
|
|
|
|
instance = LeistungstestTemplate.objects.prefetch_related('exercises__exercise').get(pk=instance.pk)
|
|
return Response(LeistungstestTemplateSerializer(instance).data)
|
|
|
|
|
|
class LeistungstestTemplateExerciseViewSet(viewsets.ModelViewSet):
|
|
queryset = LeistungstestTemplateExercise.objects.all()
|
|
serializer_class = LeistungstestTemplateExerciseSerializer
|
|
|
|
|
|
class LeistungstestResultViewSet(viewsets.ModelViewSet):
|
|
queryset = LeistungstestResult.objects.all()
|
|
serializer_class = LeistungstestResultSerializer
|
|
|
|
def get_queryset(self):
|
|
queryset = LeistungstestResult.objects.all().prefetch_related('items__exercise')
|
|
|
|
template_id = self.request.query_params.get('template')
|
|
wrestler_id = self.request.query_params.get('wrestler')
|
|
|
|
if template_id:
|
|
queryset = queryset.filter(template_id=template_id)
|
|
if wrestler_id:
|
|
queryset = queryset.filter(wrestler_id=wrestler_id)
|
|
|
|
return queryset
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
template_id = request.data.get('template')
|
|
wrestler_id = request.data.get('wrestler')
|
|
items_data = request.data.get('items', [])
|
|
|
|
result = LeistungstestResult.objects.create(
|
|
template_id=template_id,
|
|
wrestler_id=wrestler_id,
|
|
total_time_seconds=request.data.get('total_time_seconds') or None,
|
|
rating=request.data.get('rating', 3),
|
|
notes=request.data.get('notes', ''),
|
|
)
|
|
|
|
for i, item_data in enumerate(items_data):
|
|
LeistungstestResultItem.objects.create(
|
|
result=result,
|
|
exercise_id=item_data['exercise'],
|
|
target_reps=item_data.get('target_reps', 0),
|
|
actual_reps=item_data.get('actual_reps', 0),
|
|
elapsed_seconds=item_data.get('elapsed_seconds', 0),
|
|
order=item_data.get('order', i),
|
|
)
|
|
|
|
result_items = LeistungstestResultItem.objects.filter(result=result)
|
|
total_target = sum(item.target_reps for item in result_items)
|
|
total_actual = sum(item.actual_reps for item in result_items)
|
|
|
|
if total_target > 0:
|
|
score = round((total_actual / total_target) * 100, 1)
|
|
else:
|
|
score = 0.0
|
|
|
|
result.refresh_from_db()
|
|
|
|
result_data = LeistungstestResultSerializer(result).data
|
|
result_data['score_percent'] = score
|
|
|
|
return Response(result_data, status=status.HTTP_201_CREATED)
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
partial = kwargs.pop('partial', False)
|
|
instance = self.get_object()
|
|
|
|
instance.total_time_seconds = request.data.get('total_time_seconds', instance.total_time_seconds)
|
|
instance.rating = request.data.get('rating', instance.rating)
|
|
instance.notes = request.data.get('notes', instance.notes)
|
|
instance.save()
|
|
|
|
items_data = request.data.get('items')
|
|
if items_data is not None:
|
|
instance.items.all().delete()
|
|
for i, item_data in enumerate(items_data):
|
|
LeistungstestResultItem.objects.create(
|
|
result=instance,
|
|
exercise_id=item_data['exercise'],
|
|
target_reps=item_data.get('target_reps', 0),
|
|
actual_reps=item_data.get('actual_reps', 0),
|
|
elapsed_seconds=item_data.get('elapsed_seconds', 0),
|
|
order=item_data.get('order', i),
|
|
)
|
|
|
|
result_items = LeistungstestResultItem.objects.filter(result=instance)
|
|
total_target = sum(item.target_reps for item in result_items)
|
|
total_actual = sum(item.actual_reps for item in result_items)
|
|
|
|
if total_target > 0:
|
|
score = round((total_actual / total_target) * 100, 1)
|
|
else:
|
|
score = 0.0
|
|
|
|
result_data = LeistungstestResultSerializer(instance).data
|
|
result_data['score_percent'] = score
|
|
|
|
return Response(result_data)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def leaderboard(self, request):
|
|
template_id = request.query_params.get('template')
|
|
if not template_id:
|
|
return Response(
|
|
{'error': 'template parameter is required'},
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
)
|
|
|
|
limit = int(request.query_params.get('limit', 10))
|
|
|
|
results = LeistungstestResult.objects.filter(template_id=template_id)\
|
|
.select_related('wrestler')
|
|
|
|
leaderboard_data = []
|
|
for result in results:
|
|
leaderboard_data.append({
|
|
'rank': 0,
|
|
'result_id': result.id,
|
|
'wrestler_id': result.wrestler.id,
|
|
'wrestler_name': str(result.wrestler),
|
|
'score_percent': result.score_percent,
|
|
'completed_at': result.completed_at,
|
|
'total_time_seconds': result.total_time_seconds,
|
|
'rating': result.rating,
|
|
})
|
|
|
|
leaderboard_data.sort(key=lambda x: x['score_percent'], reverse=True)
|
|
leaderboard_data = leaderboard_data[:limit]
|
|
for i, entry in enumerate(leaderboard_data, 1):
|
|
entry['rank'] = i
|
|
|
|
return Response(leaderboard_data)
|
|
|
|
|
|
class LeistungstestResultItemViewSet(viewsets.ModelViewSet):
|
|
queryset = LeistungstestResultItem.objects.all()
|
|
serializer_class = LeistungstestResultItemSerializer
|
|
|
|
|
|
class LeistungstestStatsViewSet(viewsets.ViewSet):
|
|
permission_classes = [IsAuthenticated]
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def leaderboard(self, request):
|
|
lb_type = request.query_params.get('type', 'template')
|
|
template_id = request.query_params.get('template_id')
|
|
exercise_id = request.query_params.get('exercise_id')
|
|
period = request.query_params.get('period', 'all')
|
|
limit = int(request.query_params.get('limit', 10))
|
|
|
|
if lb_type == 'template' and template_id:
|
|
results = get_template_leaderboard(int(template_id), period, limit)
|
|
template = LeistungstestTemplate.objects.get(pk=template_id)
|
|
return Response({
|
|
'template_id': template_id,
|
|
'template_name': template.name,
|
|
'period': period,
|
|
'results': results,
|
|
})
|
|
elif lb_type == 'exercise' and exercise_id:
|
|
from exercises.models import Exercise
|
|
results = get_exercise_leaderboard(int(exercise_id), period, limit)
|
|
exercise = Exercise.objects.get(pk=exercise_id)
|
|
return Response({
|
|
'exercise_id': exercise_id,
|
|
'exercise_name': exercise.name,
|
|
'period': period,
|
|
'results': results,
|
|
})
|
|
return Response({'error': 'Invalid parameters'}, status=400)
|
|
|
|
@action(detail=False, methods=['get'])
|
|
def exercises(self, request):
|
|
exercises = get_used_exercises()
|
|
return Response([{'id': e.id, 'name': e.name} for e in exercises])
|