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
View File
+7
View File
@@ -0,0 +1,7 @@
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wrestleDesk.settings')
application = get_asgi_application()
+6
View File
@@ -0,0 +1,6 @@
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = 'page_size'
max_page_size = 100
+152
View File
@@ -0,0 +1,152 @@
from datetime import timedelta
import environ
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env(
DEBUG=(bool, False)
)
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG', default=True)
ALLOWED_HOSTS = env('ALLOWED_HOSTS', default='localhost,127.0.0.1,testserver').split(',')
INSTALLED_APPS = [
'unfold',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt',
'django_filters',
'corsheaders',
'drf_spectacular',
'import_export',
'django_cleanup',
'django_resized',
'auth_app',
'clubs',
'wrestlers',
'exercises',
'trainings',
'homework',
'templates',
'trainers',
'locations',
'stats',
'training_log',
'leistungstest',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'wrestleDesk.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'wrestleDesk.wsgi.application'
DATABASES = {
'default': env.db('DATABASE_URL', default=f'sqlite:///{BASE_DIR / "db.sqlite3"}')
}
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'},
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter',
'rest_framework.filters.OrderingFilter',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'AUTH_HEADER_TYPES': ('Bearer',),
}
SPECTACULAR_SETTINGS = {
'TITLE': 'WrestleDesk API',
'DESCRIPTION': 'Wrestling Club Management System API',
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
}
CORS_ALLOWED_ORIGINS = env('CORS_ALLOWED_ORIGINS', default='')
if CORS_ALLOWED_ORIGINS:
CORS_ALLOWED_ORIGINS = CORS_ALLOWED_ORIGINS.split(',')
else:
if not DEBUG:
raise ValueError("CORS_ALLOWED_ORIGINS must be explicitly configured in production")
CORS_ALLOWED_ORIGINS = ['http://localhost:3000', 'http://127.0.0.1:3000', 'http://localhost:5173', 'http://127.0.0.1:5173']
CORS_ALLOW_CREDENTIALS = True
UNFOLD = {
"SIDEBAR": {
"show_all_applications": True,
},
}
+54
View File
@@ -0,0 +1,54 @@
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from rest_framework.routers import DefaultRouter
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from clubs.views import ClubViewSet
from wrestlers.views import WrestlerViewSet
from trainers.views import TrainerViewSet
from locations.views import LocationViewSet
from exercises.views import ExerciseViewSet
from templates.views import TrainingTemplateViewSet, TemplateExerciseViewSet
from trainings.views import TrainingViewSet, AttendanceViewSet, TrainingExerciseViewSet
from homework.views import HomeworkViewSet, HomeworkExerciseItemViewSet, HomeworkAssignmentViewSet, HomeworkStatusViewSet, TrainingHomeworkAssignmentViewSet
from auth_app.views import login, register, refresh_token, me, user_preferences
from stats.views import dashboard_stats
from leistungstest.views import LeistungstestStatsViewSet
router = DefaultRouter()
router.register(r'clubs', ClubViewSet, basename='club')
router.register(r'wrestlers', WrestlerViewSet, basename='wrestler')
router.register(r'trainers', TrainerViewSet, basename='trainer')
router.register(r'locations', LocationViewSet, basename='location')
router.register(r'exercises', ExerciseViewSet, basename='exercise')
router.register(r'templates', TrainingTemplateViewSet, basename='template')
router.register(r'template-exercises', TemplateExerciseViewSet, basename='template-exercise')
router.register(r'trainings', TrainingViewSet, basename='training')
router.register(r'attendances', AttendanceViewSet, basename='attendance')
router.register(r'training-exercises', TrainingExerciseViewSet, basename='training-exercise')
router.register(r'homework', HomeworkViewSet, basename='homework')
router.register(r'homework-exercise-items', HomeworkExerciseItemViewSet, basename='homework-exercise-item')
router.register(r'homework-assignments', HomeworkAssignmentViewSet, basename='homework-assignment')
router.register(r'homework-status', HomeworkStatusViewSet, basename='homework-status')
router.register(r'training-assignments', TrainingHomeworkAssignmentViewSet, basename='training-assignment')
router.register(r'leistungstest-stats', LeistungstestStatsViewSet, basename='leistungstest-stats')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
path('api/v1/auth/login/', login, name='login'),
path('api/v1/auth/register/', register, name='register'),
path('api/v1/auth/refresh/', refresh_token, name='refresh_token'),
path('api/v1/auth/me/', me, name='me'),
path('api/v1/auth/preferences/', user_preferences, name='user_preferences'),
path('api/v1/stats/dashboard/', dashboard_stats, name='dashboard-stats'),
path('api/v1/training-log/', include('training_log.urls')),
path('api/v1/leistungstest/', include('leistungstest.urls')),
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
path('api/docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+7
View File
@@ -0,0 +1,7 @@
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wrestleDesk.settings')
application = get_wsgi_application()