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,188 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Homework(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
description = models.TextField(blank=True)
|
||||
club = models.ForeignKey('clubs.Club', on_delete=models.CASCADE, related_name='homework_templates', null=True, blank=True)
|
||||
due_date = models.DateField(null=True, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['due_date']),
|
||||
models.Index(fields=['club']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class HomeworkExerciseItem(models.Model):
|
||||
homework = models.ForeignKey(Homework, on_delete=models.CASCADE, related_name='exercise_items')
|
||||
exercise = models.ForeignKey('exercises.Exercise', on_delete=models.CASCADE, related_name='homework_items')
|
||||
reps = models.PositiveIntegerField(null=True, blank=True)
|
||||
time_minutes = models.PositiveIntegerField(null=True, blank=True)
|
||||
order = models.IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ['homework', 'order']
|
||||
unique_together = ['homework', 'exercise']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.homework.title} - {self.exercise.name}"
|
||||
|
||||
|
||||
class HomeworkAssignment(models.Model):
|
||||
homework = models.ForeignKey(Homework, on_delete=models.CASCADE, related_name='assignments')
|
||||
wrestler = models.ForeignKey('wrestlers.Wrestler', on_delete=models.CASCADE, related_name='homework_assignments')
|
||||
club = models.ForeignKey('clubs.Club', on_delete=models.CASCADE, related_name='homework_assignments', null=True, blank=True)
|
||||
due_date = models.DateField(null=True, blank=True)
|
||||
notes = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['homework', 'wrestler']
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['wrestler']),
|
||||
models.Index(fields=['due_date']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.wrestler} - {self.homework.title}"
|
||||
|
||||
def clean(self):
|
||||
from wrestlers.models import Wrestler
|
||||
if self.wrestler and self.homework and self.wrestler.club_id != self.homework.club_id:
|
||||
from django.core.exceptions import ValidationError
|
||||
raise ValidationError('Wrestler must belong to the same club as the homework')
|
||||
|
||||
@property
|
||||
def is_completed(self):
|
||||
items = self.items.all()
|
||||
if not items.exists():
|
||||
return False
|
||||
return all(item.is_completed for item in items)
|
||||
|
||||
@property
|
||||
def completion_date(self):
|
||||
if self.is_completed:
|
||||
return self.items.filter(is_completed=True).order_by('-completion_date').first().completion_date
|
||||
return None
|
||||
|
||||
|
||||
class HomeworkAssignmentItem(models.Model):
|
||||
assignment = models.ForeignKey(HomeworkAssignment, on_delete=models.CASCADE, related_name='items')
|
||||
exercise = models.ForeignKey('exercises.Exercise', on_delete=models.CASCADE, related_name='assignment_items')
|
||||
is_completed = models.BooleanField(default=False)
|
||||
completion_date = models.DateField(null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['assignment', 'exercise']
|
||||
indexes = [
|
||||
models.Index(fields=['assignment', 'is_completed']),
|
||||
models.Index(fields=['completion_date']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
status = "✓" if self.is_completed else "✗"
|
||||
return f"{self.assignment} - {self.exercise.name} {status}"
|
||||
|
||||
|
||||
class HomeworkStatus(models.Model):
|
||||
homework = models.ForeignKey(Homework, on_delete=models.CASCADE, related_name='statuses')
|
||||
wrestler = models.ForeignKey('wrestlers.Wrestler', on_delete=models.CASCADE, related_name='homework_statuses')
|
||||
is_completed = models.BooleanField(default=False)
|
||||
completion_date = models.DateField(null=True, blank=True)
|
||||
notes = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['homework', 'wrestler']
|
||||
|
||||
def __str__(self):
|
||||
status = "✓" if self.is_completed else "✗"
|
||||
return f"{self.wrestler} - {self.homework.title} {status}"
|
||||
|
||||
|
||||
# NEUES SYSTEM: Training-basierte Homework
|
||||
# Jeder Wrestler bekommt individuelle Übungen zugewiesen
|
||||
|
||||
class TrainingHomeworkAssignment(models.Model):
|
||||
"""A homework assignment for a specific wrestler in a specific training"""
|
||||
training = models.ForeignKey('trainings.Training', on_delete=models.CASCADE, related_name='homework_assignments', default=1)
|
||||
wrestler = models.ForeignKey('wrestlers.Wrestler', on_delete=models.CASCADE, related_name='training_homework_assignments')
|
||||
club = models.ForeignKey('clubs.Club', on_delete=models.CASCADE, related_name='training_homework_assignments', null=True, blank=True)
|
||||
notes = models.TextField(blank=True)
|
||||
is_completed = models.BooleanField(default=False)
|
||||
completion_date = models.DateField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['training', 'wrestler']
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['training']),
|
||||
models.Index(fields=['wrestler']),
|
||||
models.Index(fields=['is_completed']),
|
||||
models.Index(fields=['club']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.wrestler} - Training {self.training_id}"
|
||||
|
||||
|
||||
class TrainingHomeworkExerciseItem(models.Model):
|
||||
"""Individual exercises assigned to a specific wrestler (NOT shared)"""
|
||||
assignment = models.ForeignKey(TrainingHomeworkAssignment, on_delete=models.CASCADE, related_name='exercises')
|
||||
exercise = models.ForeignKey('exercises.Exercise', on_delete=models.CASCADE, related_name='training_homework_exercises')
|
||||
reps = models.PositiveIntegerField(null=True, blank=True)
|
||||
time_minutes = models.PositiveIntegerField(null=True, blank=True)
|
||||
order = models.IntegerField(default=0)
|
||||
is_completed = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
ordering = ['assignment', 'order']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.assignment} - {self.exercise.name}"
|
||||
|
||||
|
||||
# ALTES SYSTEM (für Rückwärtskompatibilität - wird nicht mehr verwendet)
|
||||
|
||||
class TrainingHomework(models.Model):
|
||||
"""DEPRECATED: Each wrestler now has individual assignments"""
|
||||
training = models.ForeignKey('trainings.Training', on_delete=models.CASCADE, related_name='homework_legacy')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['training']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"Homework for Training {self.training_id}"
|
||||
|
||||
|
||||
class TrainingHomeworkExercise(models.Model):
|
||||
"""DEPRECATED: Exercises are now per-assignment"""
|
||||
training_homework = models.ForeignKey(TrainingHomework, on_delete=models.CASCADE, related_name='exercises')
|
||||
exercise = models.ForeignKey('exercises.Exercise', on_delete=models.CASCADE, related_name='training_homework_items')
|
||||
reps = models.PositiveIntegerField(null=True, blank=True)
|
||||
time_minutes = models.PositiveIntegerField(null=True, blank=True)
|
||||
order = models.IntegerField(default=0)
|
||||
|
||||
class Meta:
|
||||
ordering = ['training_homework', 'order']
|
||||
unique_together = ['training_homework', 'exercise']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.training_homework} - {self.exercise.name}"
|
||||
Reference in New Issue
Block a user