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:
@@ -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)
|
||||
Reference in New Issue
Block a user