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
+237
View File
@@ -0,0 +1,237 @@
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from django.utils import timezone
from django.db import transaction
from utils.permissions import ClubLevelPermission, ClubFilterBackend
from .models import (
Homework, HomeworkExerciseItem, HomeworkAssignment,
HomeworkAssignmentItem, HomeworkStatus,
TrainingHomeworkAssignment, TrainingHomeworkExerciseItem
)
from .serializers import (
HomeworkSerializer, HomeworkDetailSerializer, HomeworkExerciseItemSerializer,
HomeworkAssignmentSerializer, HomeworkAssignmentListSerializer,
AssignHomeworkSerializer, CompleteItemSerializer, HomeworkStatusSerializer,
TrainingHomeworkAssignmentSerializer, TrainingHomeworkAssignmentCreateSerializer
)
from wrestleDesk.pagination import StandardResultsSetPagination
class HomeworkViewSet(viewsets.ModelViewSet):
queryset = Homework.objects.prefetch_related('exercise_items', 'exercise_items__exercise').all()
serializer_class = HomeworkSerializer
pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['club', 'is_active']
search_fields = ['title', 'description']
ordering_fields = ['created_at', 'due_date']
def get_serializer_class(self):
if self.action == 'retrieve':
return HomeworkDetailSerializer
return HomeworkSerializer
class HomeworkExerciseItemViewSet(viewsets.ModelViewSet):
queryset = HomeworkExerciseItem.objects.select_related('homework', 'exercise').all()
serializer_class = HomeworkExerciseItemSerializer
pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ['homework']
class HomeworkAssignmentViewSet(viewsets.ModelViewSet):
queryset = HomeworkAssignment.objects.select_related('homework', 'wrestler', 'club').prefetch_related('items').all()
serializer_class = HomeworkAssignmentSerializer
pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['homework', 'wrestler', 'club', 'is_completed']
search_fields = ['wrestler__first_name', 'wrestler__last_name']
ordering_fields = ['created_at', 'due_date']
def get_serializer_class(self):
if self.action == 'list':
return HomeworkAssignmentListSerializer
return HomeworkAssignmentSerializer
@action(detail=True, methods=['post'])
@transaction.atomic
def complete_item(self, request, pk=None):
serializer = CompleteItemSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
assignment = self.get_object()
item_id = serializer.validated_data['item_id']
try:
item = assignment.items.get(id=item_id)
except HomeworkAssignmentItem.DoesNotExist:
return Response(
{'detail': 'Item not found'},
status=status.HTTP_404_NOT_FOUND
)
item.is_completed = True
item.completion_date = timezone.now()
item.save()
return Response({'status': 'item completed'})
@action(detail=False, methods=['post'])
@transaction.atomic
def assign(self, request):
serializer = AssignHomeworkSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
homework_id = serializer.validated_data['homework']
wrestler_ids = serializer.validated_data['wrestlers']
due_date = serializer.validated_data.get('due_date')
notes = serializer.validated_data.get('notes', '')
try:
homework = Homework.objects.get(id=homework_id)
except Homework.DoesNotExist:
return Response({'detail': 'Homework not found'}, status=status.HTTP_404_NOT_FOUND)
created_assignments = []
errors = []
for wrestler_id in wrestler_ids:
try:
existing = HomeworkAssignment.objects.filter(
homework=homework,
wrestler_id=wrestler_id
).exists()
if existing:
errors.append(f"Wrestler {wrestler_id} hat bereits diese Hausaufgabe")
continue
assignment = HomeworkAssignment.objects.create(
homework=homework,
wrestler_id=wrestler_id,
club=homework.club,
due_date=due_date,
notes=notes
)
# Copy exercises from homework to assignment
for exercise_item in homework.exercise_items.all():
HomeworkAssignmentItem.objects.create(
assignment=assignment,
exercise=exercise_item.exercise
)
created_assignments.append(assignment.id)
except Exception as e:
errors.append(f"Wrestler {wrestler_id}: {str(e)}")
return Response({
'created': created_assignments,
'errors': errors
})
class HomeworkStatusViewSet(viewsets.ModelViewSet):
queryset = HomeworkStatus.objects.select_related('homework', 'wrestler').all()
serializer_class = HomeworkStatusSerializer
pagination_class = StandardResultsSetPagination
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
filterset_fields = ['homework', 'wrestler', 'is_completed']
ordering_fields = ['created_at', 'completion_date']
# NEUES SYSTEM: Training-basierte Homework
class TrainingHomeworkAssignmentViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
serializer_class = TrainingHomeworkAssignmentSerializer
filterset_fields = ['is_completed', 'training', 'wrestler']
search_fields = ['wrestler__first_name', 'wrestler__last_name']
ordering_fields = ['created_at', 'is_completed']
http_method_names = ['get', 'post', 'patch', 'delete']
def get_queryset(self):
queryset = TrainingHomeworkAssignment.objects.select_related(
'training', 'wrestler'
).prefetch_related(
'exercises', 'exercises__exercise'
).all()
# Filter by training ID if provided
training_id = self.request.query_params.get('training')
if training_id:
queryset = queryset.filter(training_id=training_id)
return queryset
def get_serializer_class(self):
if self.action == 'create':
return TrainingHomeworkAssignmentCreateSerializer
return TrainingHomeworkAssignmentSerializer
@action(detail=True, methods=['post'])
def complete(self, request, pk=None):
assignment = self.get_object()
assignment.is_completed = True
assignment.completion_date = timezone.now().date()
assignment.save()
# Also mark all exercises as completed
assignment.exercises.update(is_completed=True)
serializer = self.get_serializer(assignment)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def uncomplete(self, request, pk=None):
assignment = self.get_object()
assignment.is_completed = False
assignment.completion_date = None
assignment.save()
# Also mark all exercises as not completed
assignment.exercises.update(is_completed=False)
serializer = self.get_serializer(assignment)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def complete_exercise(self, request, pk=None):
assignment = self.get_object()
exercise_id = request.data.get('exercise_id')
if not exercise_id:
return Response({'detail': 'exercise_id is required'}, status=status.HTTP_400_BAD_REQUEST)
try:
item = assignment.exercises.get(id=exercise_id)
item.is_completed = True
item.save()
return Response({'status': 'exercise completed'})
except TrainingHomeworkExerciseItem.DoesNotExist:
return Response({'detail': 'Exercise not found'}, status=status.HTTP_404_NOT_FOUND)
@action(detail=True, methods=['post'])
def uncomplete_exercise(self, request, pk=None):
assignment = self.get_object()
exercise_id = request.data.get('exercise_id')
if not exercise_id:
return Response({'detail': 'exercise_id is required'}, status=status.HTTP_400_BAD_REQUEST)
try:
item = assignment.exercises.get(id=exercise_id)
item.is_completed = False
item.save()
return Response({'status': 'exercise uncompleted'})
except TrainingHomeworkExerciseItem.DoesNotExist:
return Response({'detail': 'Exercise not found'}, status=status.HTTP_404_NOT_FOUND)